mixpanel-browser
Version:
The official Mixpanel JavaScript browser client library
1,570 lines (1,392 loc) • 325 kB
JavaScript
(function () {
'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;
}