'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 = $('
'); $el.appendTo($('body')); for (var i = envs.length - 1; i >= 0; i -= 1) { var env = envs[i]; $el.addClass('hidden-' + env); if ($el.is(':hidden')) { $el.remove(); return env; } } }, isMobile: function () { var env = utils.findBootstrapEnvironment(); return ['xs', 'sm'].some(function (targetEnv) { return targetEnv === env; }); }, getHoursArray: function () { var currentHour = new Date().getHours(); var labels = []; for (var i = currentHour, ii = currentHour - 24; i > ii; i -= 1) { var hour = i < 0 ? 24 + i : i; labels.push(hour + ':00'); } return labels.reverse(); }, getDaysArray: function (from) { var currentDay = new Date(from || Date.now()).getTime(); var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var labels = []; var tmpDate; for (var x = 29; x >= 0; x -= 1) { tmpDate = new Date(currentDay - (1000 * 60 * 60 * 24 * x)); labels.push(months[tmpDate.getMonth()] + ' ' + tmpDate.getDate()); } return labels; }, /* Retrieved from http://stackoverflow.com/a/7557433 @ 27 Mar 2016 */ isElementInViewport: function (el) { // special bonus for those using jQuery if (typeof jQuery === 'function' && el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */ rect.right <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ ); }, // get all the url params in a single key/value hash params: function (options) { var a; var hash = {}; var params; options = options || {}; options.skipToType = options.skipToType || {}; if (options.url) { a = utils.urlToLocation(options.url); } params = (a ? a.search : window.location.search).substring(1).split('&'); params.forEach(function (param) { var val = param.split('='); var key = decodeURI(val[0]); var value = options.skipToType[key] ? decodeURI(val[1]) : utils.toType(decodeURI(val[1])); if (key) { if (key.substr(-2, 2) === '[]') { key = key.slice(0, -2); } if (!hash[key]) { hash[key] = value; } else { if (!$.isArray(hash[key])) { hash[key] = [hash[key]]; } hash[key].push(value); } } }); return hash; }, param: function (key) { return this.params()[key]; }, urlToLocation: function (url) { var a = document.createElement('a'); a.href = url; return a; }, // return boolean if string 'true' or string 'false', or if a parsable string which is a number // also supports JSON object and/or arrays parsing toType: function (str) { var type = typeof str; if (type !== 'string') { return str; } var nb = parseFloat(str); if (!isNaN(nb) && isFinite(str)) { return nb; } if (str === 'false') { return false; } if (str === 'true') { return true; } try { str = JSON.parse(str); } catch (e) {} return str; }, // Safely get/set chained properties on an object // set example: utils.props(A, 'a.b.c.d', 10) // sets A to {a: {b: {c: {d: 10}}}}, and returns 10 // get example: utils.props(A, 'a.b.c') // returns {d: 10} // get example: utils.props(A, 'a.b.c.foo.bar') // returns undefined without throwing a TypeError // credits to github.com/gkindel props: function (obj, props, value) { if (obj === undefined) { obj = window; } if (props == null) { return undefined; } var i = props.indexOf('.'); if (i === -1) { if (value !== undefined) { obj[props] = value; } return obj[props]; } var prop = props.slice(0, i); var newProps = props.slice(i + 1); if (props !== undefined && !(obj[prop] instanceof Object)) { obj[prop] = {}; } return utils.props(obj[prop], newProps, value); }, isInternalURI: function (targetLocation, referenceLocation, relative_path) { return targetLocation.host === '' || // Relative paths are always internal links ( targetLocation.host === referenceLocation.host && targetLocation.protocol === referenceLocation.protocol && // Otherwise need to check if protocol and host match (relative_path.length > 0 ? targetLocation.pathname.indexOf(relative_path) === 0 : true) // Subfolder installs need this additional check ); }, }; /* eslint "no-extend-native": "off" */ if (typeof String.prototype.startsWith !== 'function') { String.prototype.startsWith = function (prefix) { if (this.length < prefix.length) { return false; } return this.slice(0, prefix.length) === prefix; }; } if (typeof String.prototype.endsWith !== 'function') { String.prototype.endsWith = function (suffix) { if (this.length < suffix.length) { return false; } if (suffix.length === 0) { return true; } return this.slice(-suffix.length) === suffix; }; } if (typeof String.prototype.rtrim !== 'function') { String.prototype.rtrim = function () { return this.replace(/\s+$/g, ''); }; } return utils; }));