// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; var AsyncClient = require('./async_client.jsx'); var client = require('./client.jsx'); var Autolinker = require('autolinker'); module.exports.isEmail = function(email) { var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/; return regex.test(email); }; module.exports.cleanUpUrlable = function(input) { var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); cleaned = cleaned.replace(/^\-+/, ''); cleaned = cleaned.replace(/\-+$/, ''); return cleaned; }; module.exports.isTestDomain = function() { if ((/^localhost/).test(window.location.hostname)) { return true; } if ((/^dockerhost/).test(window.location.hostname)) { return true; } if ((/^test/).test(window.location.hostname)) { return true; } if ((/^127.0./).test(window.location.hostname)) { return true; } if ((/^192.168./).test(window.location.hostname)) { return true; } if ((/^10./).test(window.location.hostname)) { return true; } if ((/^176./).test(window.location.hostname)) { return true; } return false; }; function getSubDomain() { if (module.exports.isTestDomain()) { return ''; } if ((/^www/).test(window.location.hostname)) { return ''; } if ((/^beta/).test(window.location.hostname)) { return ''; } if ((/^ci/).test(window.location.hostname)) { return ''; } var parts = window.location.hostname.split('.'); if (parts.length !== 3) { return ''; } return parts[0]; } global.window.getSubDomain = getSubDomain; module.exports.getSubDomain = getSubDomain; module.exports.getDomainWithOutSub = function() { var parts = window.location.host.split('.'); if (parts.length === 1) { if (parts[0].indexOf('dockerhost') > -1) { return 'dockerhost:8065'; } return 'localhost:8065'; } return parts[1] + '.' + parts[2]; }; module.exports.getCookie = function(name) { var value = '; ' + document.cookie; var parts = value.split('; ' + name + '='); if (parts.length === 2) { return parts.pop().split(';').shift(); } }; module.exports.notifyMe = function(title, body, channel) { if ('Notification' in window && Notification.permission !== 'denied') { Notification.requestPermission(function onRequestPermission(permission) { if (Notification.permission !== permission) { Notification.permission = permission; } if (permission === 'granted') { var notification = new Notification(title, {body: body, tag: body, icon: '/static/images/icon50x50.gif'}); notification.onclick = function onClick() { window.focus(); if (channel) { module.exports.switchChannel(channel); } else { window.location.href = '/'; } }; setTimeout(function closeNotificationOnTimeout() { notification.close(); }, 5000); } }); } }; module.exports.ding = function() { if (!module.exports.isBrowserFirefox()) { var audio = new Audio('/static/images/ding.mp3'); audio.play(); } }; module.exports.getUrlParameter = function(sParam) { var sPageURL = window.location.search.substring(1); var sURLVariables = sPageURL.split('&'); for (var i = 0; i < sURLVariables.length; i++) { var sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { return sParameterName[1]; } } return null; }; module.exports.getDateForUnixTicks = function(ticks) { return new Date(ticks); }; module.exports.displayDate = function(ticks) { var d = new Date(ticks); var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; return monthNames[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); }; module.exports.displayTime = function(ticks) { var d = new Date(ticks); var hours = d.getHours(); var minutes = d.getMinutes(); var ampm = 'AM'; if (hours >= 12) { ampm = 'PM'; } hours = hours % 12; if (!hours) { hours = '12'; } if (minutes <= 9) { minutes = 0 + minutes; } return hours + ':' + minutes + ' ' + ampm; }; module.exports.displayDateTime = function(ticks) { var seconds = Math.floor((Date.now() - ticks) / 1000); var interval = Math.floor(seconds / 3600); if (interval > 24) { return this.displayTime(ticks); } if (interval > 1) { return interval + ' hours ago'; } if (interval === 1) { return interval + ' hour ago'; } interval = Math.floor(seconds / 60); if (interval > 1) { return interval + ' minutes ago'; } return '1 minute ago'; }; module.exports.displayCommentDateTime = function(ticks) { return module.exports.displayDate(ticks) + ' ' + module.exports.displayTime(ticks); } // returns Unix timestamp in milliseconds module.exports.getTimestamp = function() { return Date.now(); }; function testUrlMatch(text) { var urlMatcher = new Autolinker.matchParser.MatchParser({ urls: true, emails: false, twitter: false, phone: false, hashtag: false }); var result = []; function replaceFn(match) { var linkData = {}; var matchText = match.getMatchedText(); linkData.text = matchText; if (matchText.trim().indexOf('http') !== 0) { linkData.link = 'http://' + matchText; } else { linkData.link = matchText; } result.push(linkData); } urlMatcher.replace(text, replaceFn, this); return result; } module.exports.extractLinks = function(text) { var repRegex = new RegExp('
', 'g'); var matches = testUrlMatch(text.replace(repRegex, '\n')); if (!matches.length) { return {links: null, text: text}; } var links = []; for (var i = 0; i < matches.length; i++) { links.push(matches[i].link); } return {links: links, text: text}; }; module.exports.escapeRegExp = function(string) { return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); }; function getYoutubeEmbed(link) { var regex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; var youtubeId = link.trim().match(regex)[1]; function onClick(e) { var div = $(e.target).closest('.video-thumbnail__container')[0]; var iframe = document.createElement('iframe'); iframe.setAttribute('src', 'https://www.youtube.com/embed/' + div.id + '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1'); iframe.setAttribute('width', '480px'); iframe.setAttribute('height', '360px'); iframe.setAttribute('type', 'text/html'); iframe.setAttribute('frameborder', '0'); iframe.setAttribute('allowfullscreen', 'allowfullscreen'); div.parentNode.replaceChild(iframe, div); } function success(data) { if (!data.items.length || !data.items[0].snippet) { return; } var metadata = data.items[0].snippet; $('.video-uploader.' + youtubeId).html(metadata.channelTitle); $('.video-title.' + youtubeId).find('a').html(metadata.title); $('.post-list-holder-by-time').scrollTop($('.post-list-holder-by-time')[0].scrollHeight); $('.post-list-holder-by-time').perfectScrollbar('update'); } if (config.GoogleDeveloperKey) { $.ajax({ async: true, url: 'https://www.googleapis.com/youtube/v3/videos', type: 'GET', data: {part: 'snippet', id: youtubeId, key:config.GoogleDeveloperKey}, success: success }); } return (

YouTube

); } module.exports.getEmbed = function(link) { var ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/; var match = link.trim().match(ytRegex); if (match && match[1].length === 11) { return getYoutubeEmbed(link); } // Generl embed feature turned off for now return ''; // NEEDS REFACTORING WHEN TURNED BACK ON /* var id = parseInt((Math.random() * 1000000) + 1); $.ajax({ type: 'GET', url: 'https://query.yahooapis.com/v1/public/yql', data: { q: 'select * from html where url="' + link + "\" and xpath='html/head'", format: 'json' }, async: true }).done(function(data) { if(!data.query.results) { return; } var headerData = data.query.results.head; var description = '' for(var i = 0; i < headerData.meta.length; i++) { if(headerData.meta[i].name && (headerData.meta[i].name === 'description' || headerData.meta[i].name === 'Description')){ description = headerData.meta[i].content; break; } } $('.embed-title.'+id).html(headerData.title); $('.embed-description.'+id).html(description); }) return (

{link}

); */ }; module.exports.areStatesEqual = function(state1, state2) { return JSON.stringify(state1) === JSON.stringify(state2); }; module.exports.replaceHtmlEntities = function(text) { var tagsToReplace = { '&': '&', '<': '<', '>': '>' }; var newtext = text; for (var tag in tagsToReplace) { if ({}.hasOwnProperty.call(tagsToReplace, tag)) { var regex = new RegExp(tag, 'g'); newtext = newtext.replace(regex, tagsToReplace[tag]); } } return newtext; }; module.exports.insertHtmlEntities = function(text) { var tagsToReplace = { '&': '&', '<': '<', '>': '>' }; var newtext = text; for (var tag in tagsToReplace) { if ({}.hasOwnProperty.call(tagsToReplace, tag)) { var regex = new RegExp(tag, 'g'); newtext = newtext.replace(regex, tagsToReplace[tag]); } } return newtext; }; module.exports.searchForTerm = function(term) { AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_SEARCH_TERM, term: term, do_search: true }); }; var puncStartRegex = /^((?![@#])\W)+/g; var puncEndRegex = /(\W)+$/g; module.exports.textToJsx = function(text, options) { if (options && options['singleline']) { var repRegex = new RegExp('\n', 'g'); text = text.replace(repRegex, ' '); } var searchTerm = '' if (options && options['searchTerm']) { searchTerm = options['searchTerm'].toLowerCase() } var mentionClass = 'mention-highlight'; if (options && options['noMentionHighlight']) { mentionClass = ''; } var inner = []; // Function specific regex var hashRegex = /^href="#[^']+"|(#[A-Za-z]+[A-Za-z0-9_\-]*[A-Za-z0-9])$/g; var implicitKeywords = UserStore.getCurrentMentionKeys(); var lines = text.split('\n'); for (var i = 0; i < lines.length; i++) { var line = lines[i]; var words = line.split(' '); var highlightSearchClass = ''; for (var z = 0; z < words.length; z++) { var word = words[z]; var trimWord = word.replace(puncStartRegex, '').replace(puncEndRegex, '').trim(); var mentionRegex = /^(?:@)([a-z0-9_]+)$/gi; // looks loop invariant but a weird JS bug needs it to be redefined here var explicitMention = mentionRegex.exec(trimWord); if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != '') { highlightSearchClass = ' search-highlight'; } if (explicitMention && (UserStore.getProfileByUsername(explicitMention[1]) || Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1)) { var name = explicitMention[1]; // do both a non-case sensitive and case senstive check var mClass = implicitKeywords.indexOf('@'+name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@'+name) !== -1 ? mentionClass : ''; var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); if (searchTerm === name) { highlightSearchClass = ' search-highlight'; } inner.push({prefix}@{name}{suffix} ); } else if (testUrlMatch(word).length) { var match = testUrlMatch(word)[0]; var link = match.link; var prefix = word.substring(0,word.indexOf(match.text)); var suffix = word.substring(word.indexOf(match.text)+match.text.length); inner.push({prefix}{match.text}{suffix} ); } else if (trimWord.match(hashRegex)) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); var mClass = implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1 ? mentionClass : ''; if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) { highlightSearchClass = ' search-highlight'; } inner.push({prefix}{trimWord}{suffix} ); } else if (implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); if (trimWord.charAt(0) === '@') { if (searchTerm === trimWord.substring(1).toLowerCase()) { highlightSearchClass = ' search-highlight'; } inner.push({prefix}{trimWord}{suffix} ); } else { inner.push({prefix}{module.exports.replaceHtmlEntities(trimWord)}{suffix} ); } } else if (word === '') { // if word is empty dont include a span } else { inner.push({module.exports.replaceHtmlEntities(word)} ); } highlightSearchClass = ''; } if (i != lines.length-1) inner.push(
); } return inner; } module.exports.getFileType = function(extin) { var ext = extin.toLowerCase(); if (Constants.IMAGE_TYPES.indexOf(ext) > -1) { return 'image'; } if (Constants.AUDIO_TYPES.indexOf(ext) > -1) { return 'audio'; } if (Constants.VIDEO_TYPES.indexOf(ext) > -1) { return 'video'; } if (Constants.SPREADSHEET_TYPES.indexOf(ext) > -1) { return 'spreadsheet'; } if (Constants.CODE_TYPES.indexOf(ext) > -1) { return 'code'; } if (Constants.WORD_TYPES.indexOf(ext) > -1) { return 'word'; } if (Constants.EXCEL_TYPES.indexOf(ext) > -1) { return 'excel'; } if (Constants.PDF_TYPES.indexOf(ext) > -1) { return 'pdf'; } if (Constants.PATCH_TYPES.indexOf(ext) > -1) { return 'patch'; } return 'other'; }; module.exports.getPreviewImagePathForFileType = function(fileTypeIn) { var fileType = fileTypeIn.toLowerCase(); var icon; if (fileType in Constants.ICON_FROM_TYPE) { icon = Constants.ICON_FROM_TYPE[fileType]; } else { icon = Constants.ICON_FROM_TYPE.other; } return '/static/images/icons/' + icon + '.png'; }; module.exports.getIconClassName = function(fileTypeIn) { var fileType = fileTypeIn.toLowerCase(); if (fileType in Constants.ICON_FROM_TYPE) { return Constants.ICON_FROM_TYPE[fileType]; } return 'glyphicon-file'; }; module.exports.splitFileLocation = function(fileLocation) { var fileSplit = fileLocation.split('.'); var ext = ''; if (fileSplit.length > 1) { ext = fileSplit[fileSplit.length - 1]; fileSplit.splice(fileSplit.length - 1, 1); } var filePath = fileSplit.join('.'); var filename = filePath.split('/')[filePath.split('/').length - 1]; return {ext: ext, name: filename, path: filePath}; }; // Asynchronously gets the size of a file by requesting its headers. If successful, it calls the // provided callback with the file size in bytes as the argument. module.exports.getFileSize = function(url, callback) { var request = new XMLHttpRequest(); request.open('HEAD', url, true); request.onreadystatechange = function onReadyStateChange() { if (request.readyState === 4 && request.status === 200) { if (callback) { callback(parseInt(request.getResponseHeader('content-length'), 10)); } } }; request.send(); }; module.exports.toTitleCase = function(str) { function doTitleCase(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); } return str.replace(/\w\S*/g, doTitleCase); }; module.exports.changeCss = function(className, classValue) { // we need invisible container to store additional css definitions var cssMainContainer = $('#css-modifier-container'); if (cssMainContainer.length === 0) { cssMainContainer = $('
'); cssMainContainer.hide(); cssMainContainer.appendTo($('body')); } // and we need one div for each class var classContainer = cssMainContainer.find('div[data-class="' + className + '"]'); if (classContainer.length === 0) { classContainer = $('
'); classContainer.appendTo(cssMainContainer); } // append additional style classContainer.html(''); }; module.exports.rgb2hex = function(rgbIn) { if (/^#[0-9A-F]{6}$/i.test(rgbIn)) { return rgbIn; } var rgb = rgbIn.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); function hex(x) { return ('0' + parseInt(x, 10).toString(16)).slice(-2); } return '#' + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); }; module.exports.placeCaretAtEnd = function(el) { el.focus(); if (typeof window.getSelection != 'undefined' && typeof document.createRange != 'undefined') { var range = document.createRange(); range.selectNodeContents(el); range.collapse(false); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (typeof document.body.createTextRange != 'undefined') { var textRange = document.body.createTextRange(); textRange.moveToElementText(el); textRange.collapse(false); textRange.select(); } }; module.exports.getCaretPosition = function(el) { if (el.selectionStart) { return el.selectionStart; } else if (document.selection) { el.focus(); var r = document.selection.createRange(); if (r == null) { return 0; } var re = el.createTextRange(); var rc = re.duplicate(); re.moveToBookmark(r.getBookmark()); rc.setEndPoint('EndToStart', re); return rc.text.length; } return 0; }; module.exports.setSelectionRange = function(input, selectionStart, selectionEnd) { if (input.setSelectionRange) { input.focus(); input.setSelectionRange(selectionStart, selectionEnd); } else if (input.createTextRange) { var range = input.createTextRange(); range.collapse(true); range.moveEnd('character', selectionEnd); range.moveStart('character', selectionStart); range.select(); } }; module.exports.setCaretPosition = function(input, pos) { module.exports.setSelectionRange(input, pos, pos); }; module.exports.getSelectedText = function(input) { var selectedText; if (typeof document.selection !== 'undefined') { input.focus(); var sel = document.selection.createRange(); selectedText = sel.text; } else if (typeof input.selectionStart !== 'undefined') { var startPos = input.selectionStart; var endPos = input.selectionEnd; selectedText = input.value.substring(startPos, endPos); } return selectedText; }; module.exports.isValidUsername = function(name) { var error = ''; if (!name) { error = 'This field is required'; } else if (name.length < 3 || name.length > 15) { error = 'Must be between 3 and 15 characters'; } else if (!(/^[a-z0-9\.\-\_]+$/).test(name)) { error = "Must contain only lowercase letters, numbers, and the symbols '.', '-', and '_'."; } else if (!(/[a-z]/).test(name.charAt(0))) { error = 'First character must be a letter.'; } else { var lowerName = name.toLowerCase().trim(); for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) { if (lowerName === Constants.RESERVED_USERNAMES[i]) { error = 'Cannot use a reserved word as a username.'; break; } } } return error; }; function updateTabTitle(name) { document.title = name + ' ' + document.title.substring(document.title.lastIndexOf('-')); } module.exports.updateTabTitle = updateTabTitle; function updateAddressBar(channelName) { var teamURL = window.location.href.split('/channels')[0]; history.replaceState('data', '', teamURL + '/channels/' + channelName); } module.exports.updateAddressBar = updateAddressBar; function switchChannel(channel, teammateName) { AppDispatcher.handleViewAction({ type: ActionTypes.CLICK_CHANNEL, name: channel.name, id: channel.id }); updateAddressBar(channel.name); if (channel.type === 'D' && teammateName) { updateTabTitle(teammateName); } else { updateTabTitle(channel.display_name); } AsyncClient.getChannels(true, true, true); AsyncClient.getChannelExtraInfo(true); AsyncClient.getPosts(true, channel.id, Constants.POST_CHUNK_SIZE); $('.inner__wrap').removeClass('move--right'); $('.sidebar--left').removeClass('move--right'); client.trackPage(); return false; } module.exports.switchChannel = switchChannel; module.exports.isMobile = function() { return screen.width <= 768; }; module.exports.isComment = function(post) { if ('root_id' in post) { return post.root_id !== ''; } return false; }; module.exports.getDirectTeammate = function(channelId) { var userIds = ChannelStore.get(channelId).name.split('__'); var curUserId = UserStore.getCurrentId(); var teammate = {}; if (userIds.length !== 2 || userIds.indexOf(curUserId) === -1) { return teammate; } for (var idx in userIds) { if (userIds[idx] !== curUserId) { teammate = UserStore.getProfile(userIds[idx]); break; } } return teammate; }; Image.prototype.load = function(url, progressCallback) { var self = this; var xmlHTTP = new XMLHttpRequest(); xmlHTTP.open('GET', url, true); xmlHTTP.responseType = 'arraybuffer'; xmlHTTP.onload = function onLoad() { var h = xmlHTTP.getAllResponseHeaders(); var m = h.match(/^Content-Type\:\s*(.*?)$/mi); var mimeType = m[1] || 'image/png'; var blob = new Blob([this.response], {type: mimeType}); self.src = window.URL.createObjectURL(blob); }; xmlHTTP.onprogress = function onprogress(e) { parseInt(self.completedPercentage = (e.loaded / e.total) * 100, 10); if (progressCallback) { progressCallback(); } }; xmlHTTP.onloadstart = function onloadstart() { self.completedPercentage = 0; }; xmlHTTP.send(); }; Image.prototype.completedPercentage = 0; module.exports.changeColor = function(colourIn, amt) { var usePound = false; var col = colourIn; if (col[0] === '#') { col = col.slice(1); usePound = true; } var num = parseInt(col, 16); var r = (num >> 16) + amt; if (r > 255) { r = 255; } else if (r < 0) { r = 0; } var b = ((num >> 8) & 0x00FF) + amt; if (b > 255) { b = 255; } else if (b < 0) { b = 0; } var g = (num & 0x0000FF) + amt; if (g > 255) { g = 255; } else if (g < 0) { g = 0; } var pound = '#'; if (!usePound) { pound = ''; } return pound + String('000000' + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); }; module.exports.changeOpacity = function(oldColor, opacity) { var col = oldColor; if (col[0] === '#') { col = col.slice(1); } var r = parseInt(col.substring(0, 2), 16); var g = parseInt(col.substring(2, 4), 16); var b = parseInt(col.substring(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')'; }; module.exports.getFullName = function(user) { if (user.first_name && user.last_name) { return user.first_name + ' ' + user.last_name; } else if (user.first_name) { return user.first_name; } else if (user.last_name) { return user.last_name; } return ''; }; module.exports.getDisplayName = function(user) { if (user.nickname && user.nickname.trim().length > 0) { return user.nickname; } var fullName = module.exports.getFullName(user); if (fullName) { return fullName; } return user.username; }; //IE10 does not set window.location.origin automatically so this must be called instead when using it module.exports.getWindowLocationOrigin = function() { var windowLocationOrigin = window.location.origin; if (!windowLocationOrigin) { windowLocationOrigin = window.location.protocol + '//' + window.location.hostname; if (window.location.port) { windowLocationOrigin += ':' + window.location.port; } } return windowLocationOrigin; }; // Converts a file size in bytes into a human-readable string of the form '123MB'. module.exports.fileSizeToString = function(bytes) { // it's unlikely that we'll have files bigger than this if (bytes > 1024 * 1024 * 1024 * 1024) { return Math.floor(bytes / (1024 * 1024 * 1024 * 1024)) + 'TB'; } else if (bytes > 1024 * 1024 * 1024) { return Math.floor(bytes / (1024 * 1024 * 1024)) + 'GB'; } else if (bytes > 1024 * 1024) { return Math.floor(bytes / (1024 * 1024)) + 'MB'; } else if (bytes > 1024) { return Math.floor(bytes / 1024) + 'KB'; } return bytes + 'B'; }; // Converts a filename (like those attached to Post objects) to a url that can be used to retrieve attachments from the server. module.exports.getFileUrl = function(filename) { var url = filename; // This is a temporary patch to fix issue with old files using absolute paths if (url.indexOf('/api/v1/files/get') !== -1) { url = filename.split('/api/v1/files/get')[1]; } url = module.exports.getWindowLocationOrigin() + '/api/v1/files/get' + url; return url; }; // Gets the name of a file (including extension) from a given url or file path. module.exports.getFileName = function(path) { var split = path.split('/'); return split[split.length - 1]; }; // Generates a RFC-4122 version 4 compliant globally unique identifier. module.exports.generateId = function() { // implementation taken from http://stackoverflow.com/a/2117523 var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; id = id.replace(/[xy]/g, function replaceRandom(c) { var r = Math.floor(Math.random() * 16); var v; if (c === 'x') { v = r; } else { v = r & 0x3 | 0x8; } return v.toString(16); }); return id; }; module.exports.isBrowserFirefox = function() { return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1; }; // Used to get the id of the other user from a DM channel module.exports.getUserIdFromChannelName = function(channel) { var ids = channel.name.split('__'); var otherUserId = ''; if (ids[0] === UserStore.getCurrentId()) { otherUserId = ids[1]; } else { otherUserId = ids[0]; } return otherUserId; }; module.exports.importSlack = function(file, success, error) { var formData = new FormData(); formData.append('file', file, file.name); formData.append('filesize', file.size); formData.append('importFrom', 'slack'); client.importSlack(formData, success, error); };