UNPKG

mixpanel-browser

Version:

The official Mixpanel JavaScript browser client library

1,454 lines (1,304 loc) 236 kB
(function () { 'use strict'; var Config = { DEBUG: false, LIB_VERSION: '2.45.0' }; // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file var window$1; if (typeof(window) === 'undefined') { var loc = { hostname: '' }; window$1 = { navigator: { userAgent: '' }, document: { location: loc, referrer: '' }, screen: { width: 0, height: 0 }, location: loc }; } else { window$1 = window; } /* * Saved references to long variable names, so that closure compiler can * minimize file size. */ var ArrayProto = Array.prototype; var FuncProto = Function.prototype; var ObjProto = Object.prototype; var slice = ArrayProto.slice; var toString = ObjProto.toString; var hasOwnProperty = ObjProto.hasOwnProperty; var windowConsole = window$1.console; var navigator = window$1.navigator; var document$1 = window$1.document; var windowOpera = window$1.opera; var screen = window$1.screen; var userAgent = navigator.userAgent; var nativeBind = FuncProto.bind; var nativeForEach = ArrayProto.forEach; var nativeIndexOf = ArrayProto.indexOf; var nativeMap = ArrayProto.map; var nativeIsArray = Array.isArray; var 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) }; }; // 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() { // Time/ticks information // 1*new Date() is a cross browser version of Date.now() var T = function() { var d = 1 * new Date(), i = 0; // this while loop figures how many browser ticks go by // before 1*new Date() returns a new number, ie the amount // of ticks that go by per millisecond while (d == 1 * new Date()) { i++; } return d.toString(16) + i.toString(16); }; // Math.Random entropy var R = function() { return Math.random().toString(16).replace('.', ''); }; // User agent entropy // This function takes the user agent string, and then xors // together each sequence of 8 bytes. This produces a final // sequence of 8 bytes which it returns as hex. var UA = function() { var ua = userAgent, i, ch, buffer = [], ret = 0; function xor(result, byte_array) { var j, tmp = 0; for (j = 0; j < byte_array.length; j++) { tmp |= (buffer[j] << j * 8); } return result ^ tmp; } for (i = 0; i < ua.length; i++) { ch = ua.charCodeAt(i); buffer.unshift(ch & 0xFF); if (buffer.length >= 4) { ret = xor(ret, buffer); buffer = []; } } if (buffer.length > 0) { ret = xor(ret, buffer); } return ret.toString(16); }; return function() { var se = (screen.height * screen.width).toString(16); return (T() + '-' + R() + '-' + UA() + '-' + se + '-' + T()); }; })(); // _.isBlockedUA() // This is to block various web spiders from executing our JS and // sending false tracking data var BLOCKED_UA_STRS = [ 'ahrefsbot', 'baiduspider', 'bingbot', 'bingpreview', 'facebookexternal', 'petalbot', 'pinterest', 'screaming frog', 'yahoo! slurp', 'yandexbot', // 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(/[[]/, '\\[').replace(/[\]]/, '\\]'); 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 _localStorageSupported = null; var localStorageSupported = function(storage, forceCheck) { if (_localStorageSupported !== null && !forceCheck) { return _localStorageSupported; } var supported = true; try { storage = storage || window.localStorage; 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; } _localStorageSupported = supported; return supported; }; // _.localStorage _.localStorage = { is_supported: function(force_check) { var supported = localStorageSupported(null, force_check); if (!supported) { console.error('localStorage unsupported; falling back to cookie store'); } return supported; }, error: function(msg) { console.error('localStorage error: ' + msg); }, get: function(name) { try { return window.localStorage.getItem(name); } catch (err) { _.localStorage.error(err); } return null; }, parse: function(name) { try { return _.JSONDecode(_.localStorage.get(name)) || {}; } catch (err) { // noop } return null; }, set: function(name, value) { try { window.localStorage.setItem(name, value); } catch (err) { _.localStorage.error(err); } }, remove: function(name) { try { window.localStorage.removeItem(name); } catch (err) { _.localStorage.error(err); } } }; _.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(window.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[foundCount++] = elements[k]; } } currentContext = []; currentContextIndex = 0; var checkFunction; // This function will be used to filter the elements switch (attrOperator) { case '=': // Equality checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); }; break; case '~': // Match one of space seperated words checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b' + attrValue + '\\b'))); }; break; case '|': // Match start with value followed by optional hyphen checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^' + attrValue + '-?'))); }; break; case '^': // Match starts with value checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) === 0); }; break; case '$': // Match ends with value - fails with "Warning" in Opera 7 checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); }; break; case '*': // Match ends with value checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); }; break; default: // Just test for existence of attribute checkFunction = function(e) { return e.getAttribute(attrName); }; } currentContext = []; currentContextIndex = 0; for (j = 0; j < found.length; j++) { if (checkFunction(found[j])) { currentContext[currentContextIndex++] = found[j]; } } // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue); continue; // Skip to next token } // If we get here, token is JUST an element (not a class or ID selector) tagName = token; found = []; foundCount = 0; for (j = 0; j < currentContext.length; j++) { elements = currentContext[j].getElementsByTagName(tagName); for (k = 0; k < elements.length; k++) { found[foundCount++] = elements[k]; } } currentContext = found; } return currentContext; } return function(query) { if (_.isElement(query)) { return [query]; } else if (_.isObject(query) && !_.isUndefined(query.length)) { return query; } else { return getElementsBySelector.call(this, query); } }; })(); _.info = { campaignParams: function() { var campaign_keywords = 'utm_source utm_medium utm_campaign utm_content utm_term'.split(' '), kw = '', params = {}; _.each(campaign_keywords, function(kwkey) { kw = _.getQueryParam(document$1.URL, kwkey); if (kw.length) { params[kwkey] = kw; } }); return params; }, searchEngine: function(referrer) { if (referrer.search('https?://(.*)google.([^/?]*)') === 0) { return 'google'; } else if (referrer.search('https?://(.*)bing.com') === 0) { return 'bing'; } else if (referrer.search('https?://(.*)yahoo.com') === 0) { return 'yahoo'; } else if (referrer.search('https?://(.*)duckduckgo.com') === 0) { return 'duckduckgo'; } else { return null; } }, searchInfo: function(referrer) { var search = _.info.searchEngine(referrer), param = (search != 'yahoo') ? 'q' : 'p', ret = {}; if (search !== null) { ret['$search_engine'] = search; var keyword = _.getQueryParam(referrer, param);