UNPKG

mixpanel-browser

Version:

The official Mixpanel JavaScript browser client library

1,704 lines (1,514 loc) 294 kB
'use strict'; var Config = { DEBUG: false, LIB_VERSION: '2.67.0' }; // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file var win; if (typeof(window) === 'undefined') { var loc = { hostname: '' }; win = { crypto: {randomUUID: function() {throw Error('unsupported');}}, navigator: { userAgent: '', onLine: true }, document: { createElement: function() { return {}; }, location: loc, referrer: '' }, screen: { width: 0, height: 0 }, location: loc, addEventListener: function() {}, removeEventListener: function() {} }; } else { win = window; } var setImmediate = win['setImmediate']; var builtInProp, cycle, schedulingQueue, ToString = Object.prototype.toString, timer = (typeof setImmediate !== 'undefined') ? function timer(fn) { return setImmediate(fn); } : setTimeout; // dammit, IE8. try { Object.defineProperty({},'x',{}); builtInProp = function builtInProp(obj,name,val,config) { return Object.defineProperty(obj,name,{ value: val, writable: true, configurable: config !== false }); }; } catch (err) { builtInProp = function builtInProp(obj,name,val) { obj[name] = val; return obj; }; } // Note: using a queue instead of array for efficiency schedulingQueue = (function Queue() { var first, last, item; function Item(fn,self) { this.fn = fn; this.self = self; this.next = void 0; } return { add: function add(fn,self) { item = new Item(fn,self); if (last) { last.next = item; } else { first = item; } last = item; item = void 0; }, drain: function drain() { var f = first; first = last = cycle = void 0; while (f) { f.fn.call(f.self); f = f.next; } } }; })(); function schedule(fn,self) { schedulingQueue.add(fn,self); if (!cycle) { cycle = timer(schedulingQueue.drain); } } // promise duck typing function isThenable(o) { var _then, oType = typeof o; if (o !== null && (oType === 'object' || oType === 'function')) { _then = o.then; } return typeof _then === 'function' ? _then : false; } function notify() { for (var i=0; i<this.chain.length; i++) { notifyIsolated( this, (this.state === 1) ? this.chain[i].success : this.chain[i].failure, this.chain[i] ); } this.chain.length = 0; } // NOTE: This is a separate function to isolate // the `try..catch` so that other code can be // optimized better function notifyIsolated(self,cb,chain) { var ret, _then; try { if (cb === false) { chain.reject(self.msg); } else { if (cb === true) { ret = self.msg; } else { ret = cb.call(void 0,self.msg); } if (ret === chain.promise) { chain.reject(TypeError('Promise-chain cycle')); } // eslint-disable-next-line no-cond-assign else if (_then = isThenable(ret)) { _then.call(ret,chain.resolve,chain.reject); } else { chain.resolve(ret); } } } catch (err) { chain.reject(err); } } function resolve(msg) { var _then, self = this; // already triggered? if (self.triggered) { return; } self.triggered = true; // unwrap if (self.def) { self = self.def; } try { // eslint-disable-next-line no-cond-assign if (_then = isThenable(msg)) { schedule(function(){ var defWrapper = new MakeDefWrapper(self); try { _then.call(msg, function $resolve$(){ resolve.apply(defWrapper,arguments); }, function $reject$(){ reject.apply(defWrapper,arguments); } ); } catch (err) { reject.call(defWrapper,err); } }); } else { self.msg = msg; self.state = 1; if (self.chain.length > 0) { schedule(notify,self); } } } catch (err) { reject.call(new MakeDefWrapper(self),err); } } function reject(msg) { var self = this; // already triggered? if (self.triggered) { return; } self.triggered = true; // unwrap if (self.def) { self = self.def; } self.msg = msg; self.state = 2; if (self.chain.length > 0) { schedule(notify,self); } } function iteratePromises(Constructor,arr,resolver,rejecter) { for (var idx=0; idx<arr.length; idx++) { (function IIFE(idx){ Constructor.resolve(arr[idx]) .then( function $resolver$(msg){ resolver(idx,msg); }, rejecter ); })(idx); } } function MakeDefWrapper(self) { this.def = self; this.triggered = false; } function MakeDef(self) { this.promise = self; this.state = 0; this.triggered = false; this.chain = []; this.msg = void 0; } function NpoPromise(executor) { if (typeof executor !== 'function') { throw TypeError('Not a function'); } if (this['__NPO__'] !== 0) { throw TypeError('Not a promise'); } // instance shadowing the inherited "brand" // to signal an already "initialized" promise this['__NPO__'] = 1; var def = new MakeDef(this); this['then'] = function then(success,failure) { var o = { success: typeof success === 'function' ? success : true, failure: typeof failure === 'function' ? failure : false }; // Note: `then(..)` itself can be borrowed to be used against // a different promise constructor for making the chained promise, // by substituting a different `this` binding. o.promise = new this.constructor(function extractChain(resolve,reject) { if (typeof resolve !== 'function' || typeof reject !== 'function') { throw TypeError('Not a function'); } o.resolve = resolve; o.reject = reject; }); def.chain.push(o); if (def.state !== 0) { schedule(notify,def); } return o.promise; }; this['catch'] = function $catch$(failure) { return this.then(void 0,failure); }; try { executor.call( void 0, function publicResolve(msg){ resolve.call(def,msg); }, function publicReject(msg) { reject.call(def,msg); } ); } catch (err) { reject.call(def,err); } } var PromisePrototype = builtInProp({},'constructor',NpoPromise, /*configurable=*/false ); // Note: Android 4 cannot use `Object.defineProperty(..)` here NpoPromise.prototype = PromisePrototype; // built-in "brand" to signal an "uninitialized" promise builtInProp(PromisePrototype,'__NPO__',0, /*configurable=*/false ); builtInProp(NpoPromise,'resolve',function Promise$resolve(msg) { var Constructor = this; // spec mandated checks // note: best "isPromise" check that's practical for now if (msg && typeof msg === 'object' && msg['__NPO__'] === 1) { return msg; } return new Constructor(function executor(resolve,reject){ if (typeof resolve !== 'function' || typeof reject !== 'function') { throw TypeError('Not a function'); } resolve(msg); }); }); builtInProp(NpoPromise,'reject',function Promise$reject(msg) { return new this(function executor(resolve,reject){ if (typeof resolve !== 'function' || typeof reject !== 'function') { throw TypeError('Not a function'); } reject(msg); }); }); builtInProp(NpoPromise,'all',function Promise$all(arr) { var Constructor = this; // spec mandated checks if (ToString.call(arr) !== '[object Array]') { return Constructor.reject(TypeError('Not an array')); } if (arr.length === 0) { return Constructor.resolve([]); } return new Constructor(function executor(resolve,reject){ if (typeof resolve !== 'function' || typeof reject !== 'function') { throw TypeError('Not a function'); } var len = arr.length, msgs = Array(len), count = 0; iteratePromises(Constructor,arr,function resolver(idx,msg) { msgs[idx] = msg; if (++count === len) { resolve(msgs); } },reject); }); }); builtInProp(NpoPromise,'race',function Promise$race(arr) { var Constructor = this; // spec mandated checks if (ToString.call(arr) !== '[object Array]') { return Constructor.reject(TypeError('Not an array')); } return new Constructor(function executor(resolve,reject){ if (typeof resolve !== 'function' || typeof reject !== 'function') { throw TypeError('Not a function'); } iteratePromises(Constructor,arr,function resolver(idx,msg){ resolve(msg); },reject); }); }); var PromisePolyfill; if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1) { PromisePolyfill = Promise; } else { PromisePolyfill = NpoPromise; } /* eslint camelcase: "off", eqeqeq: "off" */ // Maximum allowed session recording length var MAX_RECORDING_MS = 24 * 60 * 60 * 1000; // 24 hours /* * Saved references to long variable names, so that closure compiler can * minimize file size. */ var ArrayProto = Array.prototype, FuncProto = Function.prototype, ObjProto = Object.prototype, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty, windowConsole = win.console, navigator = win.navigator, document$1 = win.document, windowOpera = win.opera, screen = win.screen, userAgent = navigator.userAgent; var nativeBind = FuncProto.bind, nativeForEach = ArrayProto.forEach, nativeIndexOf = ArrayProto.indexOf, nativeMap = ArrayProto.map, nativeIsArray = Array.isArray, breaker = {}; var _ = { trim: function(str) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } }; // Console override var console = { /** @type {function(...*)} */ log: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { try { windowConsole.log.apply(windowConsole, arguments); } catch (err) { _.each(arguments, function(arg) { windowConsole.log(arg); }); } } }, /** @type {function(...*)} */ warn: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel warning:'].concat(_.toArray(arguments)); try { windowConsole.warn.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.warn(arg); }); } } }, /** @type {function(...*)} */ error: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel error:'].concat(_.toArray(arguments)); try { windowConsole.error.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.error(arg); }); } } }, /** @type {function(...*)} */ critical: function() { if (!_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel error:'].concat(_.toArray(arguments)); try { windowConsole.error.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.error(arg); }); } } } }; var log_func_with_prefix = function(func, prefix) { return function() { arguments[0] = '[' + prefix + '] ' + arguments[0]; return func.apply(console, arguments); }; }; var console_with_prefix = function(prefix) { return { log: log_func_with_prefix(console.log, prefix), error: log_func_with_prefix(console.error, prefix), critical: log_func_with_prefix(console.critical, prefix) }; }; var safewrap = function(f) { return function() { try { return f.apply(this, arguments); } catch (e) { console.critical('Implementation error. Please turn on debug and contact support@mixpanel.com.'); if (Config.DEBUG){ console.critical(e); } } }; }; var safewrapClass = function(klass) { var proto = klass.prototype; for (var func in proto) { if (typeof(proto[func]) === 'function') { proto[func] = safewrap(proto[func]); } } }; // UNDERSCORE // Embed part of the Underscore Library _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) { return nativeBind.apply(func, slice.call(arguments, 1)); } if (!_.isFunction(func)) { throw new TypeError(); } args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) { return func.apply(context, args.concat(slice.call(arguments))); } var ctor = {}; ctor.prototype = func.prototype; var self = new ctor(); ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (Object(result) === result) { return result; } return self; }; return bound; }; /** * @param {*=} obj * @param {function(...*)=} iterator * @param {Object=} context */ _.each = function(obj, iterator, context) { if (obj === null || obj === undefined) { return; } if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) { return; } } } else { for (var key in obj) { if (hasOwnProperty.call(obj, key)) { if (iterator.call(context, obj[key], key, obj) === breaker) { return; } } } } }; _.extend = function(obj) { _.each(slice.call(arguments, 1), function(source) { for (var prop in source) { if (source[prop] !== void 0) { obj[prop] = source[prop]; } } }); return obj; }; _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === '[object Array]'; }; // from a comment on http://dbj.org/dbj/?p=286 // fails on only one very rare and deliberate custom object: // var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }}; _.isFunction = function(f) { try { return /^\s*\bfunction\b/.test(f); } catch (x) { return false; } }; _.isArguments = function(obj) { return !!(obj && hasOwnProperty.call(obj, 'callee')); }; _.toArray = function(iterable) { if (!iterable) { return []; } if (iterable.toArray) { return iterable.toArray(); } if (_.isArray(iterable)) { return slice.call(iterable); } if (_.isArguments(iterable)) { return slice.call(iterable); } return _.values(iterable); }; _.map = function(arr, callback, context) { if (nativeMap && arr.map === nativeMap) { return arr.map(callback, context); } else { var results = []; _.each(arr, function(item) { results.push(callback.call(context, item)); }); return results; } }; _.keys = function(obj) { var results = []; if (obj === null) { return results; } _.each(obj, function(value, key) { results[results.length] = key; }); return results; }; _.values = function(obj) { var results = []; if (obj === null) { return results; } _.each(obj, function(value) { results[results.length] = value; }); return results; }; _.include = function(obj, target) { var found = false; if (obj === null) { return found; } if (nativeIndexOf && obj.indexOf === nativeIndexOf) { return obj.indexOf(target) != -1; } _.each(obj, function(value) { if (found || (found = (value === target))) { return breaker; } }); return found; }; _.includes = function(str, needle) { return str.indexOf(needle) !== -1; }; // Underscore Addons _.inherit = function(subclass, superclass) { subclass.prototype = new superclass(); subclass.prototype.constructor = subclass; subclass.superclass = superclass.prototype; return subclass; }; _.isObject = function(obj) { return (obj === Object(obj) && !_.isArray(obj)); }; _.isEmptyObject = function(obj) { if (_.isObject(obj)) { for (var key in obj) { if (hasOwnProperty.call(obj, key)) { return false; } } return true; } return false; }; _.isUndefined = function(obj) { return obj === void 0; }; _.isString = function(obj) { return toString.call(obj) == '[object String]'; }; _.isDate = function(obj) { return toString.call(obj) == '[object Date]'; }; _.isNumber = function(obj) { return toString.call(obj) == '[object Number]'; }; _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; _.encodeDates = function(obj) { _.each(obj, function(v, k) { if (_.isDate(v)) { obj[k] = _.formatDate(v); } else if (_.isObject(v)) { obj[k] = _.encodeDates(v); // recurse } }); return obj; }; _.timestamp = function() { Date.now = Date.now || function() { return +new Date; }; return Date.now(); }; _.formatDate = function(d) { // YYYY-MM-DDTHH:MM:SS in UTC function pad(n) { return n < 10 ? '0' + n : n; } return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()); }; _.strip_empty_properties = function(p) { var ret = {}; _.each(p, function(v, k) { if (_.isString(v) && v.length > 0) { ret[k] = v; } }); return ret; }; /* * this function returns a copy of object after truncating it. If * passed an Array or Object it will iterate through obj and * truncate all the values recursively. */ _.truncate = function(obj, length) { var ret; if (typeof(obj) === 'string') { ret = obj.slice(0, length); } else if (_.isArray(obj)) { ret = []; _.each(obj, function(val) { ret.push(_.truncate(val, length)); }); } else if (_.isObject(obj)) { ret = {}; _.each(obj, function(val, key) { ret[key] = _.truncate(val, length); }); } else { ret = obj; } return ret; }; _.JSONEncode = (function() { return function(mixed_val) { var value = mixed_val; var quote = function(string) { var escapable = /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; // eslint-disable-line no-control-regex var meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' }; escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function(a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; }; var str = function(key, holder) { var gap = ''; var indent = ' '; var i = 0; // The loop counter. var k = ''; // The member key. var v = ''; // The member value. var length = 0; var mind = gap; var partial = []; var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); case 'object': // If the type is 'object', we might be dealing with an object or an array or // null. // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // Iterate through all of the keys in the object. for (k in value) { if (hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{' + partial.join(',') + '' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } }; // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', { '': value }); }; })(); /** * From https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js * Slightly modified to throw a real Error rather than a POJO */ _.JSONDecode = (function() { var at, // The index of the current character ch, // The current character escapee = { '"': '"', '\\': '\\', '/': '/', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t' }, text, error = function(m) { var e = new SyntaxError(m); e.at = at; e.text = text; throw e; }, next = function(c) { // If a c parameter is provided, verify that it matches the current character. if (c && c !== ch) { error('Expected \'' + c + '\' instead of \'' + ch + '\''); } // Get the next character. When there are no more characters, // return the empty string. ch = text.charAt(at); at += 1; return ch; }, number = function() { // Parse a number value. var number, string = ''; if (ch === '-') { string = '-'; next('-'); } while (ch >= '0' && ch <= '9') { string += ch; next(); } if (ch === '.') { string += '.'; while (next() && ch >= '0' && ch <= '9') { string += ch; } } if (ch === 'e' || ch === 'E') { string += ch; next(); if (ch === '-' || ch === '+') { string += ch; next(); } while (ch >= '0' && ch <= '9') { string += ch; next(); } } number = +string; if (!isFinite(number)) { error('Bad number'); } else { return number; } }, string = function() { // Parse a string value. var hex, i, string = '', uffff; // When parsing for string values, we must look for " and \ characters. if (ch === '"') { while (next()) { if (ch === '"') { next(); return string; } if (ch === '\\') { next(); if (ch === 'u') { uffff = 0; for (i = 0; i < 4; i += 1) { hex = parseInt(next(), 16); if (!isFinite(hex)) { break; } uffff = uffff * 16 + hex; } string += String.fromCharCode(uffff); } else if (typeof escapee[ch] === 'string') { string += escapee[ch]; } else { break; } } else { string += ch; } } } error('Bad string'); }, white = function() { // Skip whitespace. while (ch && ch <= ' ') { next(); } }, word = function() { // true, false, or null. switch (ch) { case 't': next('t'); next('r'); next('u'); next('e'); return true; case 'f': next('f'); next('a'); next('l'); next('s'); next('e'); return false; case 'n': next('n'); next('u'); next('l'); next('l'); return null; } error('Unexpected "' + ch + '"'); }, value, // Placeholder for the value function. array = function() { // Parse an array value. var array = []; if (ch === '[') { next('['); white(); if (ch === ']') { next(']'); return array; // empty array } while (ch) { array.push(value()); white(); if (ch === ']') { next(']'); return array; } next(','); white(); } } error('Bad array'); }, object = function() { // Parse an object value. var key, object = {}; if (ch === '{') { next('{'); white(); if (ch === '}') { next('}'); return object; // empty object } while (ch) { key = string(); white(); next(':'); if (Object.hasOwnProperty.call(object, key)) { error('Duplicate key "' + key + '"'); } object[key] = value(); white(); if (ch === '}') { next('}'); return object; } next(','); white(); } } error('Bad object'); }; value = function() { // Parse a JSON value. It could be an object, an array, a string, // a number, or a word. white(); switch (ch) { case '{': return object(); case '[': return array(); case '"': return string(); case '-': return number(); default: return ch >= '0' && ch <= '9' ? number() : word(); } }; // Return the json_parse function. It will have access to all of the // above functions and variables. return function(source) { var result; text = source; at = 0; ch = ' '; result = value(); white(); if (ch) { error('Syntax error'); } return result; }; })(); _.base64Encode = function(data) { var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = '', tmp_arr = []; if (!data) { return data; } data = _.utf8Encode(data); do { // pack three octets into four hexets o1 = data.charCodeAt(i++); o2 = data.charCodeAt(i++); o3 = data.charCodeAt(i++); bits = o1 << 16 | o2 << 8 | o3; h1 = bits >> 18 & 0x3f; h2 = bits >> 12 & 0x3f; h3 = bits >> 6 & 0x3f; h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < data.length); enc = tmp_arr.join(''); switch (data.length % 3) { case 1: enc = enc.slice(0, -2) + '=='; break; case 2: enc = enc.slice(0, -1) + '='; break; } return enc; }; _.utf8Encode = function(string) { string = (string + '').replace(/\r\n/g, '\n').replace(/\r/g, '\n'); var utftext = '', start, end; var stringl = 0, n; start = end = 0; stringl = string.length; for (n = 0; n < stringl; n++) { var c1 = string.charCodeAt(n); var enc = null; if (c1 < 128) { end++; } else if ((c1 > 127) && (c1 < 2048)) { enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128); } else { enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128); } if (enc !== null) { if (end > start) { utftext += string.substring(start, end); } utftext += enc; start = end = n + 1; } } if (end > start) { utftext += string.substring(start, string.length); } return utftext; }; _.UUID = function() { try { // use native Crypto API when available return win['crypto']['randomUUID'](); } catch (err) { // fall back to generating our own UUID // based on https://gist.github.com/scwood/3bff42cc005cc20ab7ec98f0d8e1d59d var uuid = new Array(36); for (var i = 0; i < 36; i++) { uuid[i] = Math.floor(Math.random() * 16); } uuid[14] = 4; // set bits 12-15 of time-high-and-version to 0100 uuid[19] = uuid[19] &= -5; // set bit 6 of clock-seq-and-reserved to zero uuid[19] = uuid[19] |= (1 << 3); // set bit 7 of clock-seq-and-reserved to one uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; return _.map(uuid, function(x) { return x.toString(16); }).join(''); } }; // _.isBlockedUA() // This is to block various web spiders from executing our JS and // sending false tracking data var BLOCKED_UA_STRS = [ 'ahrefsbot', 'ahrefssiteaudit', 'amazonbot', 'baiduspider', 'bingbot', 'bingpreview', 'chrome-lighthouse', 'facebookexternal', 'petalbot', 'pinterest', 'screaming frog', 'yahoo! slurp', 'yandex', // a whole bunch of goog-specific crawlers // https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers 'adsbot-google', 'apis-google', 'duplexweb-google', 'feedfetcher-google', 'google favicon', 'google web preview', 'google-read-aloud', 'googlebot', 'googleweblight', 'mediapartners-google', 'storebot-google' ]; _.isBlockedUA = function(ua) { var i; ua = ua.toLowerCase(); for (i = 0; i < BLOCKED_UA_STRS.length; i++) { if (ua.indexOf(BLOCKED_UA_STRS[i]) !== -1) { return true; } } return false; }; /** * @param {Object=} formdata * @param {string=} arg_separator */ _.HTTPBuildQuery = function(formdata, arg_separator) { var use_val, use_key, tmp_arr = []; if (_.isUndefined(arg_separator)) { arg_separator = '&'; } _.each(formdata, function(val, key) { use_val = encodeURIComponent(val.toString()); use_key = encodeURIComponent(key); tmp_arr[tmp_arr.length] = use_key + '=' + use_val; }); return tmp_arr.join(arg_separator); }; _.getQueryParam = function(url, param) { // Expects a raw URL param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]'); var regexS = '[\\?&]' + param + '=([^&#]*)', regex = new RegExp(regexS), results = regex.exec(url); if (results === null || (results && typeof(results[1]) !== 'string' && results[1].length)) { return ''; } else { var result = results[1]; try { result = decodeURIComponent(result); } catch(err) { console.error('Skipping decoding for malformed query param: ' + result); } return result.replace(/\+/g, ' '); } }; // _.cookie // Methods partially borrowed from quirksmode.org/js/cookies.html _.cookie = { get: function(name) { var nameEQ = name + '='; var ca = document$1.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1, c.length); } if (c.indexOf(nameEQ) === 0) { return decodeURIComponent(c.substring(nameEQ.length, c.length)); } } return null; }, parse: function(name) { var cookie; try { cookie = _.JSONDecode(_.cookie.get(name)) || {}; } catch (err) { // noop } return cookie; }, set_seconds: function(name, value, seconds, is_cross_subdomain, is_secure, is_cross_site, domain_override) { var cdomain = '', expires = '', secure = ''; if (domain_override) { cdomain = '; domain=' + domain_override; } else if (is_cross_subdomain) { var domain = extract_domain(document$1.location.hostname); cdomain = domain ? '; domain=.' + domain : ''; } if (seconds) { var date = new Date(); date.setTime(date.getTime() + (seconds * 1000)); expires = '; expires=' + date.toGMTString(); } if (is_cross_site) { is_secure = true; secure = '; SameSite=None'; } if (is_secure) { secure += '; secure'; } document$1.cookie = name + '=' + encodeURIComponent(value) + expires + '; path=/' + cdomain + secure; }, set: function(name, value, days, is_cross_subdomain, is_secure, is_cross_site, domain_override) { var cdomain = '', expires = '', secure = ''; if (domain_override) { cdomain = '; domain=' + domain_override; } else if (is_cross_subdomain) { var domain = extract_domain(document$1.location.hostname); cdomain = domain ? '; domain=.' + domain : ''; } if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = '; expires=' + date.toGMTString(); } if (is_cross_site) { is_secure = true; secure = '; SameSite=None'; } if (is_secure) { secure += '; secure'; } var new_cookie_val = name + '=' + encodeURIComponent(value) + expires + '; path=/' + cdomain + secure; document$1.cookie = new_cookie_val; return new_cookie_val; }, remove: function(name, is_cross_subdomain, domain_override) { _.cookie.set(name, '', -1, is_cross_subdomain, false, false, domain_override); } }; var _testStorageSupported = function (storage) { var supported = true; try { var key = '__mplss_' + cheap_guid(8), val = 'xyz'; storage.setItem(key, val); if (storage.getItem(key) !== val) { supported = false; } storage.removeItem(key); } catch (err) { supported = false; } return supported; }; var _localStorageSupported = null; var localStorageSupported = function(storage, forceCheck) { if (_localStorageSupported !== null && !forceCheck) { return _localStorageSupported; } return _localStorageSupported = _testStorageSupported(storage || win.localStorage); }; var _sessionStorageSupported = null; var sessionStorageSupported = function(storage, forceCheck) { if (_sessionStorageSupported !== null && !forceCheck) { return _sessionStorageSupported; } return _sessionStorageSupported = _testStorageSupported(storage || win.sessionStorage); }; function _storageWrapper(storage, name, is_supported_fn) { var log_error = function(msg) { console.error(name + ' error: ' + msg); }; return { is_supported: function(forceCheck) { var supported = is_supported_fn(storage, forceCheck); if (!supported) { console.error(name + ' unsupported'); } return supported; }, error: log_error, get: function(key) { try { return storage.getItem(key); } catch (err) { log_error(err); } return null; }, parse: function(key) { try { return _.JSONDecode(storage.getItem(key)) || {}; } catch (err) { // noop } return null; }, set: function(key, value) { try { storage.setItem(key, value); } catch (err) { log_error(err); } }, remove: function(key) { try { storage.removeItem(key); } catch (err) { log_error(err); } } }; } _.localStorage = _storageWrapper(win.localStorage, 'localStorage', localStorageSupported); _.sessionStorage = _storageWrapper(win.sessionStorage, 'sessionStorage', sessionStorageSupported); _.register_event = (function() { // written by Dean Edwards, 2005 // with input from Tino Zijdel - crisp@xs4all.nl // with input from Carl Sverre - mail@carlsverre.com // with input from Mixpanel // http://dean.edwards.name/weblog/2005/10/add-event/ // https://gist.github.com/1930440 /** * @param {Object} element * @param {string} type * @param {function(...*)} handler * @param {boolean=} oldSchool * @param {boolean=} useCapture */ var register_event = function(element, type, handler, oldSchool, useCapture) { if (!element) { console.error('No valid element provided to register_event'); return; } if (element.addEventListener && !oldSchool) { element.addEventListener(type, handler, !!useCapture); } else { var ontype = 'on' + type; var old_handler = element[ontype]; // can be undefined element[ontype] = makeHandler(element, handler, old_handler); } }; function makeHandler(element, new_handler, old_handlers) { var handler = function(event) { event = event || fixEvent(win.event); // this basically happens in firefox whenever another script // overwrites the onload callback and doesn't pass the event // object to previously defined callbacks. All the browsers // that don't define window.event implement addEventListener // so the dom_loaded handler will still be fired as usual. if (!event) { return undefined; } var ret = true; var old_result, new_result; if (_.isFunction(old_handlers)) { old_result = old_handlers(event); } new_result = new_handler.call(element, event); if ((false === old_result) || (false === new_result)) { ret = false; } return ret; }; return handler; } function fixEvent(event) { if (event) { event.preventDefault = fixEvent.preventDefault; event.stopPropagation = fixEvent.stopPropagation; } return event; } fixEvent.preventDefault = function() { this.returnValue = false; }; fixEvent.stopPropagation = function() { this.cancelBubble = true; }; return register_event; })(); var TOKEN_MATCH_REGEX = new RegExp('^(\\w*)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?"?([^\\]"]*)"?\\]$'); _.dom_query = (function() { /* document.getElementsBySelector(selector) - returns an array of element objects from the current document matching the CSS selector. Selectors can contain element names, class names and ids and can be nested. For example: elements = document.getElementsBySelector('div#main p a.external') Will return an array of all 'a' elements with 'external' in their class attribute that are contained inside 'p' elements that are contained inside the 'div' element which has id="main" New in version 0.4: Support for CSS2 and CSS3 attribute selectors: See http://www.w3.org/TR/css3-selectors/#attribute-selectors Version 0.4 - Simon Willison, March 25th 2003 -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows -- Opera 7 fails Version 0.5 - Carl Sverre, Jan 7th 2013 -- Now uses jQuery-esque `hasClass` for testing class name equality. This fixes a bug related to '-' characters being considered not part of a 'word' in regex. */ function getAllChildren(e) { // Returns all children of element. Workaround required for IE5/Windows. Ugh. return e.all ? e.all : e.getElementsByTagName('*'); } var bad_whitespace = /[\t\r\n]/g; function hasClass(elem, selector) { var className = ' ' + selector + ' '; return ((' ' + elem.className + ' ').replace(bad_whitespace, ' ').indexOf(className) >= 0); } function getElementsBySelector(selector) { // Attempt to fail gracefully in lesser browsers if (!document$1.getElementsByTagName) { return []; } // Split selector in to tokens var tokens = selector.split(' '); var token, bits, tagName, found, foundCount, i, j, k, elements, currentContextIndex; var currentContext = [document$1]; for (i = 0; i < tokens.length; i++) { token = tokens[i].replace(/^\s+/, '').replace(/\s+$/, ''); if (token.indexOf('#') > -1) { // Token is an ID selector bits = token.split('#'); tagName = bits[0]; var id = bits[1]; var element = document$1.getElementById(id); if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) { // element not found or tag with that ID not found, return false return []; } // Set currentContext to contain just this element currentContext = [element]; continue; // Skip to next token } if (token.indexOf('.') > -1) { // Token contains a class selector bits = token.split('.'); tagName = bits[0]; var className = bits[1]; if (!tagName) { tagName = '*'; } // Get elements matching tag, filter them for class selector found = []; foundCount = 0; for (j = 0; j < currentContext.length; j++) { if (tagName == '*') { elements = getAllChildren(currentContext[j]); } else { elements = currentContext[j].getElementsByTagName(tagName); } for (k = 0; k < elements.length; k++) { found[foundCount++] = elements[k]; } } currentContext = []; currentContextIndex = 0; for (j = 0; j < found.length; j++) { if (found[j].className && _.isString(found[j].className) && // some SVG elements have classNames which are not strings hasClass(found[j], className) ) { currentContext[currentContextIndex++] = found[j]; } } continue; // Skip to next token } // Code to deal with attribute selectors var token_match = token.match(TOKEN_MATCH_REGEX); if (token_match) { tagName = token_match[1]; var attrName = token_match[2]; var attrOperator = token_match[3]; var attrValue = token_match[4]; if (!tagName) { tagName = '*'; } // Grab all of the tagName elements within current context found = []; foundCount = 0; for (j = 0; j < currentContext.length; j++) { if (tagName == '*') { elements = getAllChildren(currentContext[j]); } else { elements = currentContext[j].getElementsByTagName(tagName); } for (k = 0; k < elements.length; k++) { found[fou