var sendError = function (err) { console.log('Caught JS client error:'); console.dir(err); var xhr = new XMLHttpRequest(); xhr.open('POST', '/api/error/add', true); xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhr.send(JSON.stringify(err)); }; /** * Chrome and Opera fully support the HTML 5 draft spec for ErrorEvent and window.onerror. * In both of these browsers you can use window.onerror to get a full stacktrace: */ var processError = function (msg, url, lineNo, columnNo, error) { var log = {}; // Handle error objects (/w stack trace) if (msg && msg.message) { log.err = { 'msg': msg.message, 'file': !!msg.fileName ? msg.fileName : false, 'ln': !!msg.lineNumber ? msg.lineNumber : !!lineNo ? lineNo : false, 'col': !!msg.columnNumber ? msg.columnNumber : !!columnNo ? columnNo : false, 'stacktrace': !!msg.stack ? msg.stack : false, 'cause': !!url ? JSON.stringify(url) : false, 'errorObj': !!error ? error : false }; } else { log.err = { 'msg': msg }; if (!!url) { log.err.file = url; } } log.url = window.location.href; log.userAgent = window.navigator.userAgent; sendError(log); }; window.onerror = function (exception, url, lineNo, columnNo, error) { processError(exception, url); return true; // surpress error alerts in old IEs }; /** * Unfortunately Firefox, Safari and IE are still around and we have to support them too. * As the stacktrace is not available in window.onerror we have to do a little bit more work: * It turns out that the only thing we can do to get stacktraces from errors is to wrap all * of our code in a `try{ }catch(e){ }` block and then look at `e.stack`. */ function wrap(func) { // Ensure we only wrap the function once. if (!func._wrapped) { func._wrapped = function () { try{ func.apply(this, arguments); } catch(e) { var log = {}; if (e && e.message) { log.err = { 'msg': e.message, 'stacktrace': !!e.stack ? e.stack : false }; sendError(log); } throw e; } } } return func._wrapped; }; /** * By changing the global definition of addEventListener so that it automatically wraps * the callback we can automatically insert `try{ }catch(e){ }` around most code. */ var addEventListener = window.EventTarget.prototype.addEventListener; window.EventTarget.prototype.addEventListener = function (event, callback, bubble) { addEventListener.call(this, event, wrap(callback), bubble); } /** * We also need to make sure that removeEventListener keeps working. Because the argument * to addEventListener is changed, it won't. Again we only need to fix this on the prototype object: */ var removeEventListener = window.EventTarget.prototype.removeEventListener; window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) { removeEventListener.call(this, event, callback._wrapped || callback, bubble); }