Skip to content

Instantly share code, notes, and snippets.

@cheeaun
Created April 11, 2013 03:59
Show Gist options
  • Save cheeaun/5360637 to your computer and use it in GitHub Desktop.
Save cheeaun/5360637 to your computer and use it in GitHub Desktop.
Crittercism's JS lib for logging errors/exceptions
var Crittercism = function () {
function l(a) {
var a = a || {
guess: !0
}, b = a.e || null,
a = !! a.guess,
c = new l.implementation,
b = c.run(b);
return a ? c.guessAnonymousFunctions(b) : b
}
var k = null,
B = null,
m = !1,
i = function () {
try {
return "localStorage" in window && null !== window.localStorage
} catch (a) {
return !1
}
}, n = function () {
i() && (localStorage["Crittercism.last_seen"] = (new Date).toString())
}, C = function () {
var a = localStorage.getItem("Crittercism.last_seen");
if (a) try {
return new Date(a)
} catch (b) {
return null
} else return null
}, t = function () {
if (i()) try {
return localStorage.getItem("Crittercism.did")
} catch (a) {
return ""
} else return ""
},
j = null,
q = !1,
r = [],
w = 0,
u = function (a) {
q ? j.postMessage(a, "*") : r.push(a)
}, D = function () {
for (; message = r.pop();) j.postMessage(message, "*")
}, o = function (a) {
switch (a.data.type) {
case "iframeSyn":
j = a.source;
j.postMessage({
type: "clientSynAck"
}, "*");
break;
case "iframeSynAck":
j = a.source;
j.postMessage({
type: "clientAck"
}, "*");
q = !0;
D();
break;
case "iframeAck":
q = !0;
D();
break;
case "setDeviceId":
a = a.data.contents.deviceId, i() && localStorage.setItem("Crittercism.did", a)
}
};
"undefined" === typeof inTest && window.addEventListener("message",
o, !1);
document.getElementById("crittercism_iframe") && (j = document.getElementById("crittercism_iframe"), j.postMessage({
type: "clientSyn"
}, "*"));
l.implementation = function () {};
l.implementation.prototype = {
run: function (a, b) {
a = a || this.createException();
b = b || this.mode(a);
return b === "other" ? this.other(arguments.callee) : this[b](a)
},
createException: function () {
try {
this.undef()
} catch (a) {
return a
}
},
mode: function (a) {
return a.arguments && a.stack ? "chrome" : typeof a.message === "string" && typeof window !== "undefined" && window.opera ? !a.stacktrace || a.message.indexOf("\n") > -1 && a.message.split("\n").length > a.stacktrace.split("\n").length ? "opera9" : !a.stack ? "opera10a" : a.stacktrace.indexOf("called from line") < 0 ? "opera10b" : "opera11" : a.stack ? "firefox" : "other"
},
instrumentFunction: function (a, b, c) {
var a = a || window,
d = a[b];
a[b] = function () {
c.call(this, l().slice(4));
return a[b]._instrumented.apply(this, arguments)
};
a[b]._instrumented = d
},
deinstrumentFunction: function (a, b) {
if (a[b].constructor === Function && a[b]._instrumented && a[b]._instrumented.constructor ===
Function) a[b] = a[b]._instrumented
},
chrome: function (a) {
a = (a.stack + "\n").replace(/^\S[^\(]+?[\n$]/gm, "").replace(/^\s+(at eval )?at\s+/gm, "").replace(/^([^\(]+?)([\n$])/gm, "{anonymous}()@$1$2").replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, "{anonymous}()@$1").split("\n");
a.pop();
return a
},
firefox: function (a) {
return a.stack.replace(/(?:\n@:0)?\s+$/m, "").replace(/^\(/gm, "{anonymous}(").split("\n")
},
opera11: function (a) {
for (var b = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/, a = a.stacktrace.split("\n"),
c = [], d = 0, f = a.length; d < f; d = d + 2) {
var e = b.exec(a[d]);
if (e) {
var g = e[4] + ":" + e[1] + ":" + e[2],
e = e[3] || "global code",
e = e.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, "{anonymous}");
c.push(e + "@" + g + " -- " + a[d + 1].replace(/^\s+/, ""))
}
}
return c
},
opera10b: function (a) {
for (var b = /^(.*)@(.+):(\d+)$/, a = a.stacktrace.split("\n"), c = [], d = 0, f = a.length; d < f; d++) {
var e = b.exec(a[d]);
e && c.push((e[1] ? e[1] + "()" : "global code") + "@" + e[2] + ":" + e[3])
}
return c
},
opera10a: function (a) {
for (var b = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,
a = a.stacktrace.split("\n"), c = [], d = 0, f = a.length; d < f; d = d + 2) {
var e = b.exec(a[d]);
e && c.push((e[3] || "{anonymous}") + "()@" + e[2] + ":" + e[1] + " -- " + a[d + 1].replace(/^\s+/, ""))
}
return c
},
opera9: function (a) {
for (var b = /Line (\d+).*script (?:in )?(\S+)/i, a = a.message.split("\n"), c = [], d = 2, f = a.length; d < f; d = d + 2) {
var e = b.exec(a[d]);
e && c.push("{anonymous}()@" + e[2] + ":" + e[1] + " -- " + a[d + 1].replace(/^\s+/, ""))
}
return c
},
other: function (a) {
for (var b = /function\s*([\w\-$]+)?\s*\(/i, c = [], d, f; a && a.arguments && c.length < 10;) {
d = b.test(a.toString()) ?
RegExp.$1 || "{anonymous}" : "{anonymous}";
f = Array.prototype.slice.call(a.arguments || []);
c[c.length] = d + "(" + this.stringifyArguments(f) + ")";
a = a.caller
}
return c
},
stringifyArguments: function (a) {
for (var b = [], c = Array.prototype.slice, d = 0; d < a.length; ++d) {
var f = a[d];
f === void 0 ? b[d] = "undefined" : f === null ? b[d] = "null" : f.constructor && (f.constructor === Array ? b[d] = f.length < 3 ? "[" + this.stringifyArguments(f) + "]" : "[" + this.stringifyArguments(c.call(f, 0, 1)) + "..." + this.stringifyArguments(c.call(f, -1)) + "]" : f.constructor === Object ?
b[d] = "#object" : f.constructor === Function ? b[d] = "#function" : f.constructor === String ? b[d] = '"' + f + '"' : f.constructor === Number && (b[d] = f))
}
return b.join(",")
},
sourceCache: {},
ajax: function (a) {
var b = this.createXMLHTTPObject();
if (b) try {
b.open("GET", a, false);
b.send(null);
return b.responseText
} catch (c) {}
return ""
},
createXMLHTTPObject: function () {
for (var a, b = [function () {
return new XMLHttpRequest
}, function () {
return new ActiveXObject("Msxml2.XMLHTTP")
}, function () {
return new ActiveXObject("Msxml3.XMLHTTP")
}, function () {
return new ActiveXObject("Microsoft.XMLHTTP")
}
],
c = 0; c < b.length; c++) try {
a = b[c]();
this.createXMLHTTPObject = b[c];
return a
} catch (d) {}
},
isSameDomain: function (a) {
return typeof location !== "undefined" && a.indexOf(location.hostname) !== -1
},
getSource: function (a) {
a in this.sourceCache || (this.sourceCache[a] = this.ajax(a).split("\n"));
return this.sourceCache[a]
},
guessAnonymousFunctions: function (a) {
for (var b = 0; b < a.length; ++b) {
var c = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,
d = a[b],
f = /\{anonymous\}\(.*\)@(.*)/.exec(d);
if (f) {
var e = c.exec(f[1]);
if (e) {
c = e[1];
f = e[2];
e = e[3] || 0;
if (c && this.isSameDomain(c) && f) {
c = this.guessAnonymousFunction(c, f, e);
a[b] = d.replace("{anonymous}", c)
}
}
}
}
return a
},
guessAnonymousFunction: function (a, b) {
var c;
try {
c = this.findFunctionName(this.getSource(a), b)
} catch (d) {
c = "getSource failed with url: " + a + ", exception: " + d.toString()
}
return c
},
findFunctionName: function (a, b) {
for (var c = /function\s+([^(]*?)\s*\(([^)]*)\)/, d = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function\b/, f = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(?:eval|new Function)\b/, e = "", g, h = Math.min(b,
20), i, j = 0; j < h; ++j) {
g = a[b - j - 1];
i = g.indexOf("//");
i >= 0 && (g = g.substr(0, i));
if (g) {
e = g + e;
if ((g = d.exec(e)) && g[1]) return g[1];
if ((g = c.exec(e)) && g[1]) return g[1];
if ((g = f.exec(e)) && g[1]) return g[1]
}
}
return "(?)"
}
};
var E = function (a) {
for (var b = [], c = [/^crittercismErrorHandler/i, /^printStackTrace/i], d = 0, f = a.length; d < f; d++) {
for (var e = a[d], g = false, h = 0, i = c.length; h < i; h++) if (e.match(c[h])) {
g = true;
break
}
g || b.push(e)
}
return b
}, F, h = {
metadata: {}
}, x = !1,
y = function (a) {
return typeof a == "undefined" || a == null || a == ""
}, G = function () {
battery =
null;
if (navigator.battery) battery = navigator.battery.level;
else if (navigator.mozBattery) battery = navigator.mozBattery.level;
else if (navigator.webkitBattery) battery = navigator.webkitBattery.level;
return battery
}, H = function () {
u({
type: "metadata",
contents: {
app_id: k,
device_id: t(),
library_version: "pre",
device_name: "html5",
metadata: h.metadata
}
})
}, J = function () {
if (i()) try {
return JSON.parse(localStorage.getItem("Crittercism.app_state")) || {}
} catch (a) {
return h || {}
} else return h || {}
}, z = function () {
var a = J();
if (i()) {
for (var b =
h, c = 0, d = 0; d < localStorage.length; d++) {
var f = localStorage.key(d),
c = c + f.length * 2;
y(localStorage.getItem(f)) || (c = c + localStorage.getItem(localStorage.key(d)).length * 2)
}
b.local_storage = c
}
G() && (a.battery_level = G());
a.app_version = F;
return a
};
window.onerror = function (a) {
++w;
var b = E(l({
e: a,
guess: true
}));
u({
type: "crash",
contents: {
app_id: k,
app_state: z(),
breadcrumbs: s,
did: t(),
exception_name: "Error",
exception_reason: a,
library_version: "pre",
unsymbolized_stacktrace: b
}
});
return true
};
var s;
i() ? (o = localStorage.getItem("Crittercism.breadcrumbs") ||
"[]", o = JSON.parse(o)) : o = [];
s = {
current_session: [],
previous_session: o
};
var K = function () {
function a(a) {
return a < 10 ? "0" + a : a
}
var b = new Date;
return b.getUTCFullYear() + "-" + a(b.getUTCMonth() + 1) + "-" + a(b.getUTCDate()) + "T" + a(b.getUTCHours()) + ":" + a(b.getUTCMinutes()) + ":" + a(b.getUTCSeconds()) + "Z"
}, p = [],
A = null,
I = 21,
v = null,
L = function (a) {
if (p.length < 20) {
var b = a && typeof a.name == "string" ? a.name : "Unknown Exception (No name passed by caller)",
a = a && typeof a.message == "string" ? a.message : "Unknown Exception Reason (No reason passed by caller)",
c = E(l({
e: a,
guess: true
})),
b = {
type: "handled_exception",
contents: {
app_id: k,
hashed_device_id: t(),
library_version: "pre",
exceptions: [{
library_version: "pre",
exception_name: b,
exception_reason: a,
library_version: "pre",
state: z(),
unsymbolized_stacktrace: c
}
]
}
};
p.push(b)
}
return this
};
return {
init: function (a) {
m = true;
k = a.appId;
F = a.appVersion || "unspecified";
B = a._iframeSrc || "https://api.crittercism.com/html5-static/html/iframe.html";
if (document.getElementById("critterframe") == null) {
a = document.createElement("iframe");
a.id = "critterframe";
a.src = B;
a.style.display = "none";
document.body.appendChild(a)
}
if (a = x == false) if (i()) a = (a = C()) ? new Date - a >= 18E5 ? true : false : true;
else a = true;
if (a) {
u({
type: "appLoad",
contents: {
app_id: k,
did: t(),
library_version: "pre",
app_state: z()
}
});
x = true
}
n();
return this
},
setMetadata: function (a) {
if (m) {
h.metadata.username && y(a.username) && (a.username = h.metadata.username);
h.metadata = a;
H()
} else throw Error("Crittercism.init call required before setMetadata");
n();
return this
},
setValue: function (a, b) {
if (m) {
y(a) ||
(h.metadata[a] = b);
H()
} else throw Error("Crittercism.init call required before setValue");
n();
return this
},
setUsername: function (a) {
if (m) h.metadata.username = a;
else throw Error("Crittercism.init call required before setUsername");
n();
return this
},
logHandledException: function (a) {
if (m) {
L(a);
var b = function () {
v = p.length;
v > 0 && u(p.pop());
A = v != 0 || I != 0 ? window.setTimeout(b, 2E4) : null;
I = v
};
A == null && b()
} else throw Error("Crittercism.init call required before logHandledException");
n();
return this
},
leaveBreadcrumb: function (a) {
if (m) if (typeof a ==
"string") {
s.current_session.push([a, K()]);
i() && (localStorage["Crittercism.breadcrumbs"] = JSON.stringify(s.current_session))
} else throw Error("Invalid breadcrumb type; must be a string");
else throw Error("Crittercism.init call required before leaveBreadcrumb");
n();
return this
},
_dumpState: function () {
return {
appId: k,
appState: h,
breadcrumbs: s,
communicationReady: q,
crashesRecorded: w,
messageQueue: r.slice(),
lastSeen: C(),
handledExceptionQueue: p,
exceptionSendTimer: A
}
},
_reset: function () {
x = q = k = false;
h = {};
r = []
},
_crashesRecorded: function () {
return w
},
_resetQueues: function () {
r = [];
p = []
}
}
}();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment