Skip to content

Instantly share code, notes, and snippets.

@sgammon
Created February 5, 2014 21:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgammon/8833331 to your computer and use it in GitHub Desktop.
Save sgammon/8833331 to your computer and use it in GitHub Desktop.
/***
*
*
* /$$ /$$ /$$$$$ /$$$$$$
* | $$ /$$/ |__ $$ /$$__ $$
* | $$ /$$/ /$$$$$$ /$$$$$$ /$$$$$$$ | $$| $$ \__/
* | $$$$$/ /$$__ $$ /$$__ $$| $$__ $$ | $$| $$$$$$
* | $$ $$ | $$$$$$$$| $$$$$$$$| $$ \ $$ /$$ | $$ \____ $$
* | $$\ $$ | $$_____/| $$_____/| $$ | $$ | $$ | $$ /$$ \ $$
* | $$ \ $$| $$$$$$$| $$$$$$$| $$ | $$ | $$$$$$/| $$$$$$/
* |__/ \__/ \_______/ \_______/|__/ |__/ \______/ \______/
*
* made with love <3 in san francisco
* ============ v 2.3.2 ===========
*/
(function (context) {
var debug = /** @const */ false,
kn = context['Keen'] || {}, _eq = kn['_eq'] || [], _cf = kn['_cf'] || {}, _gp = kn['_gp'] || {},
log = /** @const */ debug ? context['console'] ? context['console'].log : function () {} : function () {},
cx = /** @const */ /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
rep, gap, indent, meta = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, // table of character substitutions
/**
* Format integers to have at least two digits.
*
* @param {Number} n Integer to add digits to.
* @returns {Number|string|null} Properly formatted integer.
*/
_fmt_num = (function (n) {
"use strict"; return n < 10 ? '0' + n : n;
}),
/**
* Properly quote/escape a string.
*
* @param {string} string regular, unquoted string to work on
* @returns {string} Properly quoted/escaped version of that string.
*/
quote = (function (string) {
"use strict";
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
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 + '"';
}),
/**
* Stringify a native JavaScript value, at `key` in `holder`.
*
* @param {number|string} key where to pull from `holder`
* @param {Object} holder object to pull from at `key`
* @returns {string} Stringified JavaScript value.
*/
str = (function str(key, holder) {
"use strict";
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
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);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// 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 (Object.prototype.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;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.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 ?
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}),
JSON = context['JSON'] = context['JSON'] || {
/**
* Shim function to generate JSON from a given
* JavaScript value.
*
* @param {Object} value JavaScript value to encode as JSON.
* @param {Function} replacer Function to replace values, or array of strings to select keys.
* @returns {string} Serialized JSON from the given value at `value`.
*/
stringify: (function (value, replacer, space) {
"use strict";
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i, gap = '', indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) { indent += ' '; }
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') { indent = space; }
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
}
),
/**
* Shim function to parse a JavaScript value
* from a JSON string.
*
* @param {string} text JSON string to decode.
* @param {Function} reviver Callback function to inflate special structures.
* @returns {Object} Hopefully, whatever object was encoded in JSON.
*/
parse: (function (text, reviver) {
"use strict";
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j, walk = (function (holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
} return reviver.call(holder, key, value);
});
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ? walk({'': j}, '') : j;
} throw new SyntaxError('JSON.parse'); // If the text is not JSON parseable, then a SyntaxError is thrown.
}
)
},
/**
* Converts a JavaScript `Date` to a JSON ISO-8601
* representation, suitable for transmission to Keen.
*
* @returns {null|string} If the date provided is not finite, returns `null`. Otherwise, a valid ISO-8601 string.
*/
_date_to_json = Date.prototype['toJSON'] = ((typeof Date.prototype['toJSON'] == 'function') ?
Date.prototype['toJSON'] : (function (key) {
"use strict";
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
_fmt_num(this.getUTCMonth() + 1) + '-' +
_fmt_num(this.getUTCDate()) + 'T' +
_fmt_num(this.getUTCHours()) + ':' +
_fmt_num(this.getUTCMinutes()) + ':' +
_fmt_num(this.getUTCSeconds()) + 'Z'
: null;
})),
/**
* Calculates a JSON string, given the key
* it will be mounted at. Basically a proxy
* for basic types to `this.valueOf`.
*
* @returns {Object} Native value of the basetype.
*/
_base_to_json = String.prototype['toJSON'] = Number.prototype['toJSON'] = Boolean.prototype['toJSON'] = (function (key) {
"use strict"; return this.valueOf(); // proxy to `valueOf`
}),
/**
* Gets the current client's timezone offset.
*
* @returns {Number} Timezone offset (in seconds).
*/
getTimezoneOffset = context['getTimezoneOffset'] = (function (){
"use strict"; return new Date().getTimezoneOffset() * -60;
}),
/**
* Determines whether Daylight Savings Time (DST) is active
* in the current environment and point in time.
*
* @returns {Boolean} True/False describing whether DST is currently active.
*/
stdTimezoneOffset = Date.prototype['stdTimezoneOffset'] = (function() {
"use strict";
var jan = new Date(this.getFullYear(), -1, 1),
jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}),
/**
* Determines whether Daylight Savings Time (DST) is active
* in the current environment and point in time.
*
* @returns {Boolean} True/False describing whether DST is currently active.
*/
_date_dst = Date.prototype['dst'] = (function() {
"use strict"; return getTimezoneOffset() < stdTimezoneOffset();
}),
/**
* Parses an ISO-8601 date into a vanilla javascript Date object.
* This is magic and is a modified version of this: https://github.com/csnover/js-iso8601/blob/master/iso8601.js
*
* @param date a string representation of a date in ISO-8601 format
* @returns {Date} JavaScript Date object from ISO-8601 timestamp provided at `date`.
*/
parseDate = (function (date) {
"use strict";
var numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ], timestamp, struct, minutesOffset = 0, i;
if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
for (i = 0, k; (k = numericKeys[i]); ++i) {
struct[k] = +struct[k] || 0;
}
struct[2] = (+struct[2] || 1) - 1;
struct[3] = +struct[3] || 1;
if (struct[8] !== 'Z' && struct[9] !== undefined) {
minutesOffset = struct[10] * 60 + struct[11];
if (struct[9] === '+') {
minutesOffset = 0 - minutesOffset;
}
}
timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
}
else {
timestamp = Date.parse ? Date.parse(date) : NaN;
}
return new Date(timestamp);
}),
/**
* Takes in a map of keys & values; returns ampersand-delimited query string
*
* @param params map of key value pairs.
* @returns {string} Composed query string, encoded for use in a URL, from keys/values in `params`
*/
makeQueryString = (function (params) {
"use strict";
_.each(params, function(value, key) {
if(_.isUndefined(value)) {
delete params[key];
}
});
return _.map(params, function (value, key) {
if(!_.isString(value)) {
value = JSON.stringify(value);
}
value = encodeURIComponent(value);
return key + '=' + value;
}).join('&');
}),
/** == Keen JS == **/
Keen = context['Keen'] = /**@struct */ Keen || {},
/*******************/
/**
* Configure the Keen IO JS Library with a Project ID and API Keys.
*
* @param {Object} config Contains the project ID and optional keys.
* @param {string} config.projectId the Keen IO Project ID
* @param {string} [config.writeKey] the Keen IO Scoped Write Key
* @param {string} [config.readKey] the Keen IO Scoped Read Key
* @param {string} [config.keenUrl] the base url for the Keen IO API
* @returns {Keen.Client} An instance of Keen.Client that has been configured
*/
_keen_configure = Keen['configure'] = (function (config) {
"use strict"; return (this.client = new Keen.Client(config));
}),
/**
* Add an event to Keen IO.
*
* @param {string} eventCollection The name of the event collection
* @param {Object} event The actual event to send
* @param {Function} [success] Invoked on success
* @param {Function} [error] Invoked on failure
* @returns {null}
*/
_add_event = Keen['addEvent'] = (function (eventCollection, event, success, error) {
"use strict";
return (this.client ? this.client.uploadEvent(eventCollection, event, success, error) : _eq.push([eventCollection, event, success, error]));
}),
/**
* Add an event to Keen IO before navigating to an external page/submitting form
*
* @param {Element} htmlElement The html element being clicked/submitted
* @param {string} eventCollection The name of the event to be recorded
* @param {Object} event The event properties
* @param {Integer} [timeout=500] The amount of time to wait in milliseconds before timing out
* @param {Function} [timeoutCallback] Invoked on timeout
* @returns {Boolean} Returns false to prevent a default action from taking place
*/
_track_external_link = Keen['trackExternalLink'] = (function(htmlElement, eventCollection, event, timeout, timeoutCallback){
"use strict";
var triggered = false, callback = (function(){});
timeout = timeout || 500;
if( htmlElement.nodeName === "A"){
callback = function(){
if(!triggered){
triggered = true;
window.location = htmlElement.href;
}
};
} else if (htmlElement.nodeName === "FORM"){
callback = function(){
if(!triggered){
triggered = true;
htmlElement.submit();
}
};
}
if(timeoutCallback){
callback = function(){
if(!triggered){
triggered = true;
timeoutCallback();
}
};
}
Keen.addEvent(eventCollection, event, callback, callback);
setTimeout(function() { callback(); }, timeout);
return false;
}),
/**
* Sets the global properties to use.
*
* @param {Function} newGlobalProperties A function that returns an object of properties
* @returns null
*/
_set_global_properties = Keen['setGlobalProperties'] = (function (newGlobalProperties) {
"use strict";
if (this.client) {
if (newGlobalProperties && typeof(newGlobalProperties) == "function") {
this.client.globalProperties = newGlobalProperties;
} else {
throw new Error("Invalid value for global properties: " + newGlobalProperties);
}
}
}),
/* ==== KEEN CLIENT OBJECT ==== */
_keen_client = Keen['Client'] = (function (config) {
"use strict";
this.projectId = config.projectId;
this.writeKey = config.writeKey;
this.globalProperties = null;
if (context && context.location && context.location.protocol == 'https:') {
this.keenUrl = "https://api.keen.io"; // don't want to break existing SSL
} else {
this.keenUrl = "http://api.keen.io"; // otherwise default to HTTP
}
if(config !== undefined && config.keenUrl !== undefined){
this.keenUrl = config.keenUrl;
}
}),
/**
* Uploads a single event to the Keen IO servers.
*
* @param {string} eventCollection The name of the event collection to use
* @param {Object} event The actual event properties to send
* @param {Function} [success] Invoked on success
* @param {Function} [error] Invoked on failure
* @returns {null}
*/
_upload_event = Keen.Client.prototype['uploadEvent'] = (function (eventCollection, event, success, error) {
"use strict";
var url = this.getKeenUrl("/events/" + eventCollection);
// handle global properties
var newEvent = {};
if (this.globalProperties) {
newEvent = this.globalProperties(eventCollection);
}
// now add in the properties from the user-defined event
for (var property in event) {
if (event.hasOwnProperty(property)) {
newEvent[property] = event[property];
}
}
var jsonBody = JSON.stringify(newEvent);
var base64Body = Keen.Base64.encode(jsonBody);
url = url + "?api_key=" + context.encodeURIComponent(this.writeKey);
url = url + "&data=" + context.encodeURIComponent(base64Body);
url = url + "&modified=" + context.encodeURIComponent(new Date().getTime());
url = url + "&c=clv1";
sendBeacon(url, null, success, error);
}),
/**
* Returns a full URL by appending the provided path to the root Keen IO URL.
*
* @param {string} path The path of the desired url
* @returns {string} A fully formed URL for use in an API call.
*/
_get_keen_url = Keen.Client.prototype['getKeenUrl'] = (function (path) {
"use strict";
return this.keenUrl + "/3.0/projects/" + this.projectId + path;
}),
/**
* Checks whether XMLHttpRequest is supported, facilitates fallback to JSONP.
*
* @returns {Boolean} true/false indicating XHR support and presence of `withCredentials` flag (XHR2).
*/
supportsXhr = (function () {
"use strict";
return (typeof XMLHttpRequest !== 'undefined') ? "withCredentials" in new XMLHttpRequest() : false;
}),
/**
* Send an event via an image beacon.
*
* Handles injection of an image beacon for recording
* events.
*
* @param {Object} Data to send for the event.
*/
sendBeacon = context['sendBeacon'] = (function (url, apiKey, success, error) {
// add api_key if it's not there
if (apiKey && url.indexOf("api_key") < 0) {
var delimiterChar = url.indexOf("?") > 0 ? "&" : "?";
url = url + delimiterChar + "api_key=" + apiKey;
}
var loaded = false, img = document.createElement("img");
img.onload = function() {
loaded = true;
if ('naturalHeight' in this) {
if (this.naturalHeight + this.naturalWidth === 0) {
this.onerror(); return;
}
} else if (this.width + this.height === 0) {
this.onerror(); return;
}
if (success) { success({created: true}); }
};
img.onerror = function() {
loaded = true;
if (error) {
error();
}
};
img.src = url;
}),
/**
*
* Base64 encode / decode
* https://gist.github.com/sgammon/5562296
*
**/
Base64 = context['Base64'] = Keen['Base64'] = context['Base64'] || {
map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function (n) {
"use strict";
var o = "", i = 0, m = this.map, i1, i2, i3, e1, e2, e3, e4;
n = this.utf8.encode(n);
while (i < n.length) {
i1 = n.charCodeAt(i++); i2 = n.charCodeAt(i++); i3 = n.charCodeAt(i++);
e1 = (i1 >> 2); e2 = (((i1 & 3) << 4) | (i2 >> 4)); e3 = (isNaN(i2) ? 64 : ((i2 & 15) << 2) | (i3 >> 6));
e4 = (isNaN(i2) || isNaN(i3)) ? 64 : i3 & 63;
o = o + m.charAt(e1) + m.charAt(e2) + m.charAt(e3) + m.charAt(e4);
} return o;
},
decode: function (n) {
"use strict";
var o = "", i = 0, m = this.map, cc = String.fromCharCode, e1, e2, e3, e4, c1, c2, c3;
n = n.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < n.length) {
e1 = m.indexOf(n.charAt(i++)); e2 = m.indexOf(n.charAt(i++));
e3 = m.indexOf(n.charAt(i++)); e4 = m.indexOf(n.charAt(i++));
c1 = (e1 << 2) | (e2 >> 4); c2 = ((e2 & 15) << 4) | (e3 >> 2);
c3 = ((e3 & 3) << 6) | e4;
o = o + (cc(c1) + ((e3 != 64) ? cc(c2) : "")) + (((e4 != 64) ? cc(c3) : ""));
} return this.utf8.decode(o);
},
utf8: {
encode: function (n) {
"use strict";
var o = "", i = 0, cc = String.fromCharCode, c;
while (i < n.length) {
c = n.charCodeAt(i++); o = o + ((c < 128) ? cc(c) : ((c > 127) && (c < 2048)) ?
(cc((c >> 6) | 192) + cc((c & 63) | 128)) : (cc((c >> 12) | 224) + cc(((c >> 6) & 63) | 128) + cc((c & 63) | 128)));
} return o;
},
decode: function (n) {
"use strict";
var o = "", i = 0, cc = String.fromCharCode, c2, c;
while (i < n.length) {
c = n.charCodeAt(i);
o = o + ((c < 128) ? [cc(c), i++][0] : ((c > 191) && (c < 224)) ?
[cc(((c & 31) << 6) | ((c2 = n.charCodeAt(i + 1)) & 63)), (i += 2)][0] :
[cc(((c & 15) << 12) | (((c2 = n.charCodeAt(i + 1)) & 63) << 6) | ((c3 = n.charCodeAt(i + 2)) & 63)), (i += 3)][0]);
} return o;
}
}
};
// handle any queued commands
(function (_cf, _gp, _eq) {
"use strict";
var event, eventCollection, success, error, i;
if (debug) { log('Keen ready.', Keen); }
if (_cf) { Keen.configure(_cf); }
if (_gp) { Keen.setGlobalProperties(_gp); }
if (_eq && _eq.length > 0) { for (i = 0; i < _eq.length; i++) {
eventCollection = _eq[i].shift();
event = _eq[i].shift();
success = _eq[i].shift();
error = _eq[i].shift();
Keen.addEvent(eventCollection, event, success, error);
}
}
})(_cf, _gp, _eq);
})(window || {});
/* Keen JS - minimal SDK, version 2.3.2 */
(function(m){var r=m.Keen||{},w=r._eq||[],x=r._cf||{},r=r._gp||{},t=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,u=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,q,p,y={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},s=function(f){return 10>f?"0"+f:f},v=function(f){u.lastIndex=0;return u.test(f)?'"'+f.replace(u,function(a){var d=y[a];
return"string"===typeof d?d:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+f+'"'},z=function a(d,c){var b,e,g,n,f=p,h,l=c[d];l&&"object"===typeof l&&"function"===typeof l.toJSON&&(l=l.toJSON(d));"function"===typeof q&&(l=q.call(c,d,l));switch(typeof l){case "string":return v(l);case "number":return isFinite(l)?String(l):"null";case "boolean":case "null":return String(l);case "object":if(!l)return"null";p+=void 0;h=[];if("[object Array]"===Object.prototype.toString.apply(l)){n=l.length;
for(b=0;b<n;b+=1)h[b]=a(b,l)||"null";g=0===h.length?"[]":p?"[\n"+p+h.join(",\n"+p)+"\n"+f+"]":"["+h.join(",")+"]";p=f;return g}if(q&&"object"===typeof q)for(n=q.length,b=0;b<n;b+=1)"string"===typeof q[b]&&(e=q[b],(g=a(e,l))&&h.push(v(e)+(p?": ":":")+g));else for(e in l)Object.prototype.hasOwnProperty.call(l,e)&&(g=a(e,l))&&h.push(v(e)+(p?": ":":")+g);g=0===h.length?"{}":p?"{\n"+p+h.join(",\n"+p)+"\n"+f+"}":"{"+h.join(",")+"}";p=f;return g}},A=m.JSON=m.JSON||{stringify:function(a,d,c){var b;if("number"===
typeof c)for(b=0;b<c;b+=1);if((q=d)&&"function"!==typeof d&&("object"!==typeof d||"number"!==typeof d.length))throw Error("JSON.stringify");return z("",{"":a})},parse:function(a,d){var c,b=function(a,c){var n,f,h=a[c];if(h&&"object"===typeof h)for(n in h)Object.prototype.hasOwnProperty.call(h,n)&&(f=b(h,n),void 0!==f?h[n]=f:delete h[n]);return d.call(a,c,h)};a=String(a);t.lastIndex=0;t.test(a)&&(a=a.replace(t,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return c=eval("("+a+")"),"function"===typeof d?b({"":c},""):c;throw new SyntaxError("JSON.parse");}};Date.prototype.toJSON="function"==typeof Date.prototype.toJSON?Date.prototype.toJSON:function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+s(this.getUTCMonth()+1)+"-"+s(this.getUTCDate())+"T"+s(this.getUTCHours())+":"+s(this.getUTCMinutes())+":"+s(this.getUTCSeconds())+
"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()};var B=m.getTimezoneOffset=function(){return-60*(new Date).getTimezoneOffset()},C=Date.prototype.stdTimezoneOffset=function(){var a=new Date(this.getFullYear(),-1,1),d=new Date(this.getFullYear(),6,1);return Math.max(a.getTimezoneOffset(),d.getTimezoneOffset())};Date.prototype.dst=function(){return B()<C()};var f=m.Keen=f||{};f.configure=function(a){return this.client=new f.Client(a)};
f.addEvent=function(a,d,c,b){return this.client?this.client.uploadEvent(a,d,c,b):w.push([a,d,c,b])};f.trackExternalLink=function(a,d,c,b,e){var g=!1,n=function(){};b=b||500;"A"===a.nodeName?n=function(){g||(g=!0,window.location=a.href)}:"FORM"===a.nodeName&&(n=function(){g||(g=!0,a.submit())});e&&(n=function(){g||(g=!0,e())});f.addEvent(d,c,n,n);setTimeout(function(){n()},b);return!1};f.setGlobalProperties=function(a){if(this.client)if(a&&"function"==typeof a)this.client.globalProperties=a;else throw Error("Invalid value for global properties: "+
a);};f.Client=function(a){this.projectId=a.projectId;this.writeKey=a.writeKey;this.globalProperties=null;this.keenUrl=m&&m.location&&"https:"==m.location.protocol?"https://api.keen.io":"http://api.keen.io";void 0!==a&&void 0!==a.keenUrl&&(this.keenUrl=a.keenUrl)};f.Client.prototype.uploadEvent=function(a,d,c,b){var e=this.getKeenUrl("/events/"+a),g={};this.globalProperties&&(g=this.globalProperties(a));for(var n in d)d.hasOwnProperty(n)&&(g[n]=d[n]);a=A.stringify(g);a=f.Base64.encode(a);e=e+"?api_key="+
m.encodeURIComponent(this.writeKey);e=e+"&data="+m.encodeURIComponent(a);e=e+"&modified="+m.encodeURIComponent((new Date).getTime());D(e+"&c=clv1",null,c,b)};f.Client.prototype.getKeenUrl=function(a){return this.keenUrl+"/3.0/projects/"+this.projectId+a};var D=m.sendBeacon=function(a,d,c,b){if(d&&0>a.indexOf("api_key")){var e=0<a.indexOf("?")?"&":"?";a=a+e+"api_key="+d}d=document.createElement("img");d.onload=function(){if("naturalHeight"in this){if(0===this.naturalHeight+this.naturalWidth){this.onerror();
return}}else if(0===this.width+this.height){this.onerror();return}c&&c({created:!0})};d.onerror=function(){b&&b()};d.src=a};m.Base64=f.Base64=m.Base64||{map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(a){var d="",c=0,b=this.map,e,g,f,m,h;for(a=this.utf8.encode(a);c<a.length;)e=a.charCodeAt(c++),g=a.charCodeAt(c++),f=a.charCodeAt(c++),m=e>>2,e=(e&3)<<4|g>>4,h=isNaN(g)?64:(g&15)<<2|f>>6,g=isNaN(g)||isNaN(f)?64:f&63,d=d+b.charAt(m)+b.charAt(e)+b.charAt(h)+b.charAt(g);
return d},decode:function(a){var d="",c=0,b=this.map,e=String.fromCharCode,g,f,m,h,l;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");c<a.length;)g=b.indexOf(a.charAt(c++)),f=b.indexOf(a.charAt(c++)),m=b.indexOf(a.charAt(c++)),h=b.indexOf(a.charAt(c++)),g=g<<2|f>>4,f=(f&15)<<4|m>>2,l=(m&3)<<6|h,d=d+(e(g)+(64!=m?e(f):""))+(64!=h?e(l):"");return this.utf8.decode(d)},utf8:{encode:function(a){for(var d="",c=0,b=String.fromCharCode,e;c<a.length;)e=a.charCodeAt(c++),d+=128>e?b(e):127<e&&2048>e?b(e>>6|192)+b(e&
63|128):b(e>>12|224)+b(e>>6&63|128)+b(e&63|128);return d},decode:function(a){for(var d="",c=0,b=String.fromCharCode,e;c<a.length;)e=a.charCodeAt(c),d+=128>e?[b(e),c++][0]:191<e&&224>e?[b((e&31)<<6|a.charCodeAt(c+1)&63),c+=2][0]:[b((e&15)<<12|(a.charCodeAt(c+1)&63)<<6|(c3=a.charCodeAt(c+2))&63),c+=3][0];return d}}};(function(a,d,c){var b,e,g;a&&f.configure(a);d&&f.setGlobalProperties(d);if(c&&0<c.length)for(g=0;g<c.length;g++)d=c[g].shift(),a=c[g].shift(),b=c[g].shift(),e=c[g].shift(),f.addEvent(d,
a,b,e)})(x,r,w)})(window||{});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment