diff --git a/public/javascripts/notifier.js b/public/javascripts/notifier.js new file mode 100644 index 0000000..29ee3de --- /dev/null +++ b/public/javascripts/notifier.js @@ -0,0 +1,293 @@ +var Hoptoad = { + VERSION : '2.0', + NOTICE_XML : '\ + \ + \ + \ + hoptoad_notifier_js\ + 2.0\ + http://hoptoadapp.com\ + \ + \ + EXCEPTION_CLASS\ + EXCEPTION_MESSAGE\ + BACKTRACE_LINES\ + \ + \ + REQUEST_URL\ + REQUEST_COMPONENT\ + REQUEST_ACTION\ + \ + \ + PROJECT_ROOT\ + production\ + \ + ', + ROOT : window.location.protocol + '//' + window.location.host, + BACKTRACE_MATCHER : /^(.*)\@(.*)\:(\d+)$/, + backtrace_filters : [/notifier\.js/], + + notify: function(error) { + var xml = escape(Hoptoad.generateXML(error)); + var host = Hoptoad.host || 'hoptoadapp.com'; + var url = '//' + host + '/notifier_api/v2/notices?data=' + xml; + var request = document.createElement('iframe'); + + request.style.width = '1px'; + request.style.height = '1px'; + request.style.display = 'none'; + request.src = url; + + document.getElementsByTagName('head')[0].appendChild(request); + }, + + setEnvironment: function(value) { + var matcher = /.*<\/environment-name>/; + + Hoptoad.NOTICE_XML = Hoptoad.NOTICE_XML.replace(matcher, + '' + + value + + '') + }, + + setHost: function(value) { + Hoptoad.host = value; + }, + + setKey: function(value) { + var matcher = /.*<\/api-key>/; + + Hoptoad.NOTICE_XML = Hoptoad.NOTICE_XML.replace(matcher, + '' + + value + + ''); + }, + + setErrorDefaults: function(value) { + Hoptoad.errorDefaults = value; + }, + + generateXML: function(errorWithoutDefaults) { + var error = Hoptoad.mergeDefault(Hoptoad.errorDefaults, errorWithoutDefaults); + + var xml = Hoptoad.NOTICE_XML; + var url = Hoptoad.escapeText(error.url || ''); + var component = Hoptoad.escapeText(error.component || ''); + var action = Hoptoad.escapeText(error.action || ''); + var type = Hoptoad.escapeText(error.type || 'Error'); + var message = Hoptoad.escapeText(error.message || 'Unknown error.'); + var backtrace = Hoptoad.generateBacktrace(error); + + + if (Hoptoad.trim(url) == '' && Hoptoad.trim(component) == '') { + xml = xml.replace(/.*<\/request>/, ''); + } else { + var data = ''; + + var cgi_data = error['cgi-data'] || {}; + cgi_data["HTTP_USER_AGENT"] = navigator.userAgent; + data += ''; + data += Hoptoad.generateVariables(cgi_data); + data += ''; + + var methods = ['params', 'session']; + + for (var i = 0; i < 2; i++) { + var type = methods[i]; + + if (error[type]) { + data += '<' + type + '>'; + data += Hoptoad.generateVariables(error[type]); + data += ''; + } + } + + xml = xml.replace('', data + '') + .replace('REQUEST_URL', url) + .replace('REQUEST_ACTION', action) + .replace('REQUEST_COMPONENT', component); + } + + return xml.replace('PROJECT_ROOT', Hoptoad.ROOT) + .replace('EXCEPTION_CLASS', type) + .replace('EXCEPTION_MESSAGE', message) + .replace('BACKTRACE_LINES', backtrace.join('')); + }, + + generateBacktrace: function(error) { + error = error || {}; + + if (typeof error.stack != 'string') { + try { + (0)(); + } catch(e) { + error.stack = e.stack; + } + } + + var backtrace = []; + var stacktrace = Hoptoad.getStackTrace(error); + + for (var i = 0, l = stacktrace.length; i < l; i++) { + var line = stacktrace[i]; + var matches = line.match(Hoptoad.BACKTRACE_MATCHER); + + if (matches && Hoptoad.validBacktraceLine(line)) { + var file = matches[2].replace(Hoptoad.ROOT, '[PROJECT_ROOT]'); + + if (i == 0) { + if (matches[2].match(document.location.href)) { + backtrace.push(''); + } + } + + backtrace.push(''); + } + } + + return backtrace; + }, + + getStackTrace: function(error) { + var stacktrace = printStackTrace({ e : error, guess : false }); + + for (var i = 0, l = stacktrace.length; i < l; i++) { + if (stacktrace[i].match(/\:\d+$/)) { + continue; + } + + if (stacktrace[i].indexOf('@') == -1) { + stacktrace[i] += '@unsupported.js'; + } + + stacktrace[i] += ':0'; + } + + return stacktrace; + }, + + validBacktraceLine: function(line) { + for (var i = 0; i < Hoptoad.backtrace_filters.length; i++) { + if (line.match(Hoptoad.backtrace_filters[i])) { + return false; + } + } + + return true; + }, + + generateVariables: function(parameters) { + var key; + var result = ''; + + for (key in parameters) { + result += '' + + Hoptoad.escapeText(parameters[key]) + + ''; + } + + return result; + }, + + escapeText: function(text) { + return text.replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"'); + }, + + trim: function(text) { + return text.toString().replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + mergeDefault: function(defaults, hash) { + var cloned = {}; + var key; + + for (key in hash) { + cloned[key] = hash[key]; + } + + for (key in defaults) { + if (!cloned.hasOwnProperty(key)) { + cloned[key] = defaults[key]; + } + } + + return cloned; + } +}; + + + + +// Domain Public by Eric Wendelin http://eriwen.com/ (2008) +// Luke Smith http://lucassmith.name/ (2008) +// Loic Dachary (2008) +// Johan Euphrosine (2008) +// Øyvind Sean Kinsey http://kinsey.no/blog (2010) +// +// Information and discussions +// http://jspoker.pokersource.info/skin/test-printstacktrace.html +// http://eriwen.com/javascript/js-stack-trace/ +// http://eriwen.com/javascript/stacktrace-update/ +// http://pastie.org/253058 +// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html +// +// +// guessFunctionNameFromLines comes from firebug +// +// Software License Agreement (BSD License) +// +// Copyright (c) 2007, Parakey Inc. +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the +// following disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// * Neither the name of Parakey Inc. nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior +// written permission of Parakey Inc. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +function printStackTrace(a){var b=a&&a.e?a.e:null;a=a?!!a.guess:true;var c=new printStackTrace.implementation;b=c.run(b);return a?c.guessFunctions(b):b}printStackTrace.implementation=function(){}; +printStackTrace.implementation.prototype={run:function(a){var b=this._mode||this.mode();if(b==="other")return this.other(arguments.callee);else{var c;if(!(c=a))a:{try{0()}catch(d){c=d;break a}c=void 0}a=c;return this[b](a)}},mode:function(){try{0()}catch(a){if(a.arguments)return this._mode="chrome";else if(a.stack)return this._mode="firefox";else if(window.opera&&!("stacktrace"in a))return this._mode="opera"}return this._mode="other"},chrome:function(a){return a.stack.replace(/^.*?\n/,"").replace(/^.*?\n/, +"").replace(/^.*?\n/,"").replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.\s*\(/gm,"{anonymous}()@").split("\n")},firefox:function(a){return a.stack.replace(/^.*?\n/,"").replace(/(?:\n@:0)?\s+$/m,"").replace(/^\(/gm,"{anonymous}(").split("\n")},opera:function(a){a=a.message.split("\n");var b=/Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i,c,d,e;c=4;d=0;for(e=a.length;c