'use strict'; (function (factory) { if (typeof module === 'object' && module.exports) { var winston = require('winston'); module.exports = factory(require('xregexp')); module.exports.walk = function (dir, done) { // DEPRECATED var file = require('../../src/file'); winston.warn('[deprecated] `utils.walk` is deprecated. Use `file.walk` instead.'); file.walk(dir, done); }; process.profile = function (operation, start) { console.log('%s took %d milliseconds', operation, process.elapsedTimeSince(start)); }; process.elapsedTimeSince = function (start) { var diff = process.hrtime(start); return (diff[0] * 1e3) + (diff[1] / 1e6); }; } else { window.utils = factory(window.XRegExp); } }(function (XRegExp) { var utils = { generateUUID: function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0; var v = c === 'x' ? r : ((r & 0x3) | 0x8); return v.toString(16); }); }, invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'), invalidLatinChars: /[^\w\s\d\-_]/g, trimRegex: /^\s+|\s+$/g, collapseWhitespace: /\s+/g, collapseDash: /-+/g, trimTrailingDash: /-$/g, trimLeadingDash: /^-/g, isLatin: /^[\w\d\s.,\-@]+$/, languageKeyRegex: /\[\[[\w]+:.+\]\]/, // http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/ slugify: function (str, preserveCase) { if (!str) { return ''; } str = str.replace(utils.trimRegex, ''); if (utils.isLatin.test(str)) { str = str.replace(utils.invalidLatinChars, '-'); } else { str = XRegExp.replace(str, utils.invalidUnicodeChars, '-'); } str = !preserveCase ? str.toLocaleLowerCase() : str; str = str.replace(utils.collapseWhitespace, '-'); str = str.replace(utils.collapseDash, '-'); str = str.replace(utils.trimTrailingDash, ''); str = str.replace(utils.trimLeadingDash, ''); return str; }, cleanUpTag: function (tag, maxLength) { if (typeof tag !== 'string' || !tag.length) { return ''; } tag = tag.trim().toLowerCase(); // see https://github.com/NodeBB/NodeBB/issues/4378 tag = tag.replace(/\u202E/gi, ''); tag = tag.replace(/[,/#!$%^*;:{}=_`<>'"~()?|]/g, ''); tag = tag.substr(0, maxLength || 15).trim(); var matches = tag.match(/^[.-]*(.+?)[.-]*$/); if (matches && matches.length > 1) { tag = matches[1]; } return tag; }, removePunctuation: function (str) { return str.replace(/[.,-/#!$%^&*;:{}=\-_`<>'"~()?]/g, ''); }, isEmailValid: function (email) { return typeof email === 'string' && email.length && email.indexOf('@') !== -1; }, isUserNameValid: function (name) { return (name && name !== '' && (/^['"\s\-+.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.test(name))); }, isPasswordValid: function (password) { return typeof password === 'string' && password.length; }, isNumber: function (n) { return !isNaN(parseFloat(n)) && isFinite(n); }, hasLanguageKey: function (input) { return utils.languageKeyRegex.test(input); }, // shallow objects merge merge: function () { var result = {}; var obj; var keys; for (var i = 0; i < arguments.length; i += 1) { obj = arguments[i] || {}; keys = Object.keys(obj); for (var j = 0; j < keys.length; j += 1) { result[keys[j]] = obj[keys[j]]; } } return result; }, fileExtension: function (path) { return ('' + path).split('.').pop(); }, extensionMimeTypeMap: { bmp: 'image/bmp', cmx: 'image/x-cmx', cod: 'image/cis-cod', gif: 'image/gif', ico: 'image/x-icon', ief: 'image/ief', jfif: 'image/pipeg', jpe: 'image/jpeg', jpeg: 'image/jpeg', jpg: 'image/jpeg', png: 'image/png', pbm: 'image/x-portable-bitmap', pgm: 'image/x-portable-graymap', pnm: 'image/x-portable-anymap', ppm: 'image/x-portable-pixmap', ras: 'image/x-cmu-raster', rgb: 'image/x-rgb', svg: 'image/svg+xml', tif: 'image/tiff', tiff: 'image/tiff', xbm: 'image/x-xbitmap', xpm: 'image/x-xpixmap', xwd: 'image/x-xwindowdump', }, fileMimeType: function (path) { return utils.extensionToMimeType(utils.fileExtension(path)); }, extensionToMimeType: function (extension) { return utils.extensionMimeTypeMap[extension] || '*'; }, isRelativeUrl: function (url) { var firstChar = String(url || '').charAt(0); return (firstChar === '.' || firstChar === '/'); }, makeNumbersHumanReadable: function (elements) { elements.each(function () { $(this).html(utils.makeNumberHumanReadable($(this).attr('title'))); }); }, makeNumberHumanReadable: function (num) { var n = parseInt(num, 10); if (!n) { return num; } if (n > 999999) { return (n / 1000000).toFixed(1) + 'm'; } else if (n > 999) { return (n / 1000).toFixed(1) + 'k'; } return n; }, addCommasToNumbers: function (elements) { elements.each(function (index, element) { $(element).html(utils.addCommas($(element).html())); }); }, // takes a string like 1000 and returns 1,000 addCommas: function (text) { return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); }, toISOString: function (timestamp) { if (!timestamp || !Date.prototype.toISOString) { return ''; } return Date.prototype.toISOString ? new Date(parseInt(timestamp, 10)).toISOString() : timestamp; }, tags: ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr'], stripTags: ['abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'base', 'basefont', 'bdi', 'bdo', 'big', 'blink', 'body', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'iframe', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'map', 'mark', 'marquee', 'menu', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'source', 'span', 'strike', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr'], escapeRegexChars: function (text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); }, escapeHTML: function (raw) { return raw.replace(/&/gm, '&').replace(//gm, '>'); }, isAndroidBrowser: function () { // http://stackoverflow.com/questions/9286355/how-to-detect-only-the-native-android-browser var nua = navigator.userAgent; return ((nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1) && !(nua.indexOf('Chrome') > -1)); }, isTouchDevice: function () { return 'ontouchstart' in document.documentElement; }, findBootstrapEnvironment: function () { // http://stackoverflow.com/questions/14441456/how-to-detect-which-device-view-youre-on-using-twitter-bootstrap-api var envs = ['xs', 'sm', 'md', 'lg']; var $el = $('