History

Update Raven/Sentry

Thibaut committed 12 years ago

Commit bc95cfd1 parent 76763e62 files · +9 −1588Browse files

Showing the changes to assets/javascripts (2 of 2 files). Show all

Unified · Split

2 files
  1. assets/javascripts/app/app.coffee +6 −1
  2. assets/javascripts/vendor/raven.js +3 −1587
assets/javascripts/app/app.coffee+6−1
@@ -44,7 +44,12 @@ · 43 unmodified lines
44 if @isInvalidLocation()44 if @isInvalidLocation()
45 new app.views.Notif 'InvalidLocation'45 new app.views.Notif 'InvalidLocation'
46 else46 else
47 Raven.config(@config.sentry_dsn).install() if @config.sentry_dsn47 if @config.sentry_dsn
48 Raven.config @config.sentry_dsn,
49 whitelistUrls: [/devdocs/]
50 includePaths: [/devdocs/]
51 ignoreErrors: [/dpQuery/]
52 .install()
48 @previousErrorHandler = onerror53 @previousErrorHandler = onerror
49 window.onerror = @onWindowError.bind(@)54 window.onerror = @onWindowError.bind(@)
50 return55 return
assets/javascripts/vendor/raven.js+3−1587
@@ -1,1587 +1,3 @@
1/*! Raven.js 1.0.8 | github.com/getsentry/raven-js */1/*! Raven.js 1.1.10 (e96d774) | github.com/getsentry/raven-js */
2 2!function(a,b){"use strict";function c(a,b){var c,d;b=b||{},a="raven"+a.substr(0,1).toUpperCase()+a.substr(1),document.createEvent?(c=document.createEvent("HTMLEvents"),c.initEvent(a,!0,!0)):(c=document.createEventObject(),c.eventType=a);for(d in b)b.hasOwnProperty(d)&&(c[d]=b[d]);if(document.createEvent)document.dispatchEvent(c);else try{document.fireEvent("on"+c.eventType.toLowerCase(),c)}catch(e){}}function d(a){this.name="RavenConfigError",this.message=a}function e(a){var b=L.exec(a),c={},e=7;try{for(;e--;)c[K[e]]=b[e]||""}catch(f){throw new d("Invalid DSN: "+a)}if(c.pass)throw new d("Do not specify your private key in the DSN!");return c}function f(a){return"undefined"==typeof a}function g(a){return"function"==typeof a}function h(a){return"string"==typeof a}function i(a){for(var b in a)return!1;return!0}function j(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function k(a,b){var c,d;if(f(a.length))for(c in a)a.hasOwnProperty(c)&&b.call(null,c,a[c]);else if(d=a.length)for(c=0;d>c;c++)b.call(null,c,a[c])}function l(){if(M)return M;var a=["sentry_version=4","sentry_client=raven-js/"+J.VERSION];return E&&a.push("sentry_key="+E),M="?"+a.join("&")}function m(a,b){var d=[];a.stack&&a.stack.length&&k(a.stack,function(a,b){var c=n(b);c&&d.push(c)}),c("handle",{stackInfo:a,options:b}),p(a.name,a.message,a.url,a.lineno,d,b)}function n(a){if(a.url){var b,c={filename:a.url,lineno:a.line,colno:a.column,"function":a.func||"?"},d=o(a);if(d){var e=["pre_context","context_line","post_context"];for(b=3;b--;)c[e[b]]=d[b]}return c.in_app=!(!I.includePaths.test(c.filename)||/(Raven|TraceKit)\./.test(c["function"])||/raven\.(min\.)js$/.test(c.filename)),c}}function o(a){if(a.context&&I.fetchContext){for(var b=a.context,c=~~(b.length/2),d=b.length,e=!1;d--;)if(b[d].length>300){e=!0;break}if(e){if(f(a.column))return;return[[],b[c].substr(a.column,50),[]]}return[b.slice(0,c),b[c],b.slice(c+1)]}}function p(a,b,c,d,e,f){var g,h;b&&(I.ignoreErrors.test(b)||(e&&e.length?(c=e[0].filename||c,e.reverse(),g={frames:e}):c&&(g={frames:[{filename:c,lineno:d}]}),I.ignoreUrls&&I.ignoreUrls.test(c)||(!I.whitelistUrls||I.whitelistUrls.test(c))&&(h=d?b+" at "+d:b,s(q({exception:{type:a,value:b},stacktrace:g,culprit:c,message:h},f)))))}function q(a,b){return b?(k(b,function(b,c){a[b]=c}),a):a}function r(){var a={url:document.location.href,headers:{"User-Agent":navigator.userAgent}};return document.referrer&&(a.headers.Referer=document.referrer),a}function s(a){u()&&(a=q({project:F,logger:I.logger,site:I.site,platform:"javascript",request:r()},a),a.tags=q(I.tags,a.tags),a.extra=q(I.extra,a.extra),i(a.tags)&&delete a.tags,i(a.extra)&&delete a.extra,D&&(a.user=D),g(I.dataCallback)&&(a=I.dataCallback(a)),(!g(I.shouldSendCallback)||I.shouldSendCallback(a))&&(B=a.event_id||(a.event_id=w()),t(a)))}function t(a){var b=new Image,d=C+l()+"&sentry_data="+encodeURIComponent(JSON.stringify(a));b.onload=function(){c("success",{data:a,src:d})},b.onerror=b.onabort=function(){c("failure",{data:a,src:d})},b.src=d}function u(){return H?C?!0:(a.console&&console.error&&console.error("Error: Raven has not been configured."),!1):!1}function v(a){for(var b,c=[],d=0,e=a.length;e>d;d++)b=a[d],h(b)?c.push(b.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1")):b&&b.source&&c.push(b.source);return new RegExp(c.join("|"),"i")}function w(){return"xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"==a?b:3&b|8;return c.toString(16)})}var x={remoteFetching:!1,collectWindowErrors:!0,linesOfContext:7},y=[].slice,z="?";x.wrap=function(a){function b(){try{return a.apply(this,arguments)}catch(b){throw x.report(b),b}}return b},x.report=function(){function c(a){i(),o.push(a)}function d(a){for(var b=o.length-1;b>=0;--b)o[b]===a&&o.splice(b,1)}function e(){k(),o=[]}function g(a,b){var c=null;if(!b||x.collectWindowErrors){for(var d in o)if(j(o,d))try{o[d].apply(null,[a].concat(y.call(arguments,2)))}catch(e){c=e}if(c)throw c}}function h(a,b,c,d,e){var h=null,i=!1;if(f(e))if(q)x.computeStackTrace.augmentStackTraceWithInitialElement(q,b,c,a),h=q,q=null,p=null;else{var j={url:b,line:c,column:d};j.func=x.computeStackTrace.guessFunctionName(j.url,j.line),j.context=x.computeStackTrace.gatherContext(j.url,j.line),h={mode:"onerror",message:a,url:document.location.href,stack:[j],useragent:navigator.userAgent}}else l(e,!1),i=!0;return i||g(h,"from window.onerror"),m?m.apply(this,arguments):!1}function i(){n||(m=a.onerror,a.onerror=h,n=!0)}function k(){n&&(a.onerror=m,n=!1,m=b)}function l(b,c){var d=y.call(arguments,1);if(q){if(p===b)return;var e=q;q=null,p=null,g.apply(null,[e,null].concat(d))}var f=x.computeStackTrace(b);if(q=f,p=b,a.setTimeout(function(){p===b&&(q=null,p=null,g.apply(null,[f,null].concat(d)))},f.incomplete?2e3:0),c!==!1)throw b}var m,n,o=[],p=null,q=null;return l.subscribe=c,l.unsubscribe=d,l.uninstall=e,l}(),x.computeStackTrace=function(){function b(b){if(!x.remoteFetching)return"";try{var c=function(){try{return new a.XMLHttpRequest}catch(b){return new a.ActiveXObject("Microsoft.XMLHTTP")}},d=c();return d.open("GET",b,!1),d.send(""),d.responseText}catch(e){return""}}function c(a){if(!j(u,a)){var c="";-1!==a.indexOf(document.domain)&&(c=b(a)),u[a]=c?c.split("\n"):[]}return u[a]}function d(a,b){var d,e=/function ([^(]*)\(([^)]*)\)/,g=/['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,h="",i=10,j=c(a);if(!j.length)return z;for(var k=0;i>k;++k)if(h=j[b-k]+h,!f(h)){if(d=g.exec(h))return d[1];if(d=e.exec(h))return d[1]}return z}function e(a,b){var d=c(a);if(!d.length)return null;var e=[],g=Math.floor(x.linesOfContext/2),h=g+x.linesOfContext%2,i=Math.max(0,b-g-1),j=Math.min(d.length,b+h-1);b-=1;for(var k=i;j>k;++k)f(d[k])||e.push(d[k]);return e.length>0?e:null}function g(a){return a.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g,"\\$&")}function h(a){return g(a).replace("<","(?:<|&lt;)").replace(">","(?:>|&gt;)").replace("&","(?:&|&amp;)").replace('"','(?:"|&quot;)').replace(/\s+/g,"\\s+")}function i(a,b){for(var d,e,f=0,g=b.length;g>f;++f)if((d=c(b[f])).length&&(d=d.join("\n"),e=a.exec(d)))return{url:b[f],line:d.substring(0,e.index).split("\n").length,column:e.index-d.lastIndexOf("\n",e.index)-1};return null}function k(a,b,d){var e,f=c(b),h=new RegExp("\\b"+g(a)+"\\b");return d-=1,f&&f.length>d&&(e=h.exec(f[d]))?e.index:null}function l(b){for(var c,d,e,f,j=[a.location.href],k=document.getElementsByTagName("script"),l=""+b,m=/^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,n=/^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,o=0;o<k.length;++o){var p=k[o];p.src&&j.push(p.src)}if(e=m.exec(l)){var q=e[1]?"\\s+"+e[1]:"",r=e[2].split(",").join("\\s*,\\s*");c=g(e[3]).replace(/;$/,";?"),d=new RegExp("function"+q+"\\s*\\(\\s*"+r+"\\s*\\)\\s*{\\s*"+c+"\\s*}")}else d=new RegExp(g(l).replace(/\s+/g,"\\s+"));if(f=i(d,j))return f;if(e=n.exec(l)){var s=e[1];if(c=h(e[2]),d=new RegExp("on"+s+"=[\\'\"]\\s*"+c+"\\s*[\\'\"]","i"),f=i(d,j[0]))return f;if(d=new RegExp(c),f=i(d,j))return f}return null}function m(a){if(!a.stack)return null;for(var b,c,f=/^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?((?:file|https?):.*?):(\d+)(?::(\d+))?\)?\s*$/i,g=/^\s*(\S*)(?:\((.*?)\))?@((?:file|https?).*?):(\d+)(?::(\d+))?\s*$/i,h=a.stack.split("\n"),i=[],j=/^(.*) is undefined$/.exec(a.message),l=0,m=h.length;m>l;++l){if(b=g.exec(h[l]))c={url:b[3],func:b[1]||z,args:b[2]?b[2].split(","):"",line:+b[4],column:b[5]?+b[5]:null};else{if(!(b=f.exec(h[l])))continue;c={url:b[2],func:b[1]||z,line:+b[3],column:b[4]?+b[4]:null}}!c.func&&c.line&&(c.func=d(c.url,c.line)),c.line&&(c.context=e(c.url,c.line)),i.push(c)}return i[0]&&i[0].line&&!i[0].column&&j&&(i[0].column=k(j[1],i[0].url,i[0].line)),i.length?{mode:"stack",name:a.name,message:a.message,url:document.location.href,stack:i,useragent:navigator.userAgent}:null}function n(a){for(var b,c=a.stacktrace,f=/ line (\d+), column (\d+) in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,g=c.split("\n"),h=[],i=0,j=g.length;j>i;i+=2)if(b=f.exec(g[i])){var k={line:+b[1],column:+b[2],func:b[3]||b[4],args:b[5]?b[5].split(","):[],url:b[6]};if(!k.func&&k.line&&(k.func=d(k.url,k.line)),k.line)try{k.context=e(k.url,k.line)}catch(l){}k.context||(k.context=[g[i+1]]),h.push(k)}return h.length?{mode:"stacktrace",name:a.name,message:a.message,url:document.location.href,stack:h,useragent:navigator.userAgent}:null}function o(b){var f=b.message.split("\n");if(f.length<4)return null;var g,k,l,m,n=/^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,o=/^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,p=/^\s*Line (\d+) of function script\s*$/i,q=[],r=document.getElementsByTagName("script"),s=[];for(k in r)j(r,k)&&!r[k].src&&s.push(r[k]);for(k=2,l=f.length;l>k;k+=2){var t=null;if(g=n.exec(f[k]))t={url:g[2],func:g[3],line:+g[1]};else if(g=o.exec(f[k])){t={url:g[3],func:g[4]};var u=+g[1],v=s[g[2]-1];if(v&&(m=c(t.url))){m=m.join("\n");var w=m.indexOf(v.innerText);w>=0&&(t.line=u+m.substring(0,w).split("\n").length)}}else if(g=p.exec(f[k])){var x=a.location.href.replace(/#.*$/,""),y=g[1],z=new RegExp(h(f[k+1]));m=i(z,[x]),t={url:x,line:m?m.line:y,func:""}}if(t){t.func||(t.func=d(t.url,t.line));var A=e(t.url,t.line),B=A?A[Math.floor(A.length/2)]:null;t.context=A&&B.replace(/^\s*/,"")===f[k+1].replace(/^\s*/,"")?A:[f[k+1]],q.push(t)}}return q.length?{mode:"multiline",name:b.name,message:f[0],url:document.location.href,stack:q,useragent:navigator.userAgent}:null}function p(a,b,c,f){var g={url:b,line:c};if(g.url&&g.line){a.incomplete=!1,g.func||(g.func=d(g.url,g.line)),g.context||(g.context=e(g.url,g.line));var h=/ '([^']+)' /.exec(f);if(h&&(g.column=k(h[1],g.url,g.line)),a.stack.length>0&&a.stack[0].url===g.url){if(a.stack[0].line===g.line)return!1;if(!a.stack[0].line&&a.stack[0].func===g.func)return a.stack[0].line=g.line,a.stack[0].context=g.context,!1}return a.stack.unshift(g),a.partial=!0,!0}return a.incomplete=!0,!1}function q(a,b){for(var c,e,f,g=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,h=[],i={},j=!1,m=q.caller;m&&!j;m=m.caller)if(m!==r&&m!==x.report){if(e={url:null,func:z,line:null,column:null},m.name?e.func=m.name:(c=g.exec(m.toString()))&&(e.func=c[1]),f=l(m)){e.url=f.url,e.line=f.line,e.func===z&&(e.func=d(e.url,e.line));var n=/ '([^']+)' /.exec(a.message||a.description);n&&(e.column=k(n[1],f.url,f.line))}i[""+m]?j=!0:i[""+m]=!0,h.push(e)}b&&h.splice(0,b);var o={mode:"callers",name:a.name,message:a.message,url:document.location.href,stack:h,useragent:navigator.userAgent};return p(o,a.sourceURL||a.fileName,a.line||a.lineNumber,a.message||a.description),o}function r(a,b){var c=null;b=null==b?0:+b;try{if(c=n(a))return c}catch(d){if(t)throw d}try{if(c=m(a))return c}catch(d){if(t)throw d}try{if(c=o(a))return c}catch(d){if(t)throw d}try{if(c=q(a,b+1))return c}catch(d){if(t)throw d}return{mode:"failed"}}function s(a){a=(null==a?0:+a)+1;try{throw new Error}catch(b){return r(b,a+1)}}var t=!1,u={};return r.augmentStackTraceWithInitialElement=p,r.guessFunctionName=d,r.gatherContext=e,r.ofCaller=s,r}();var A,B,C,D,E,F,G=a.Raven,H=!(!a.JSON||!a.JSON.stringify),I={logger:"javascript",ignoreErrors:[],ignoreUrls:[],whitelistUrls:[],includePaths:[],collectWindowErrors:!0,tags:{},extra:{}},J={VERSION:"1.1.10",TraceKit:x,afterLoad:function(){var b=a.RavenConfig;b&&this.config(b.dsn,b.config).install()},noConflict:function(){return a.Raven=G,J},config:function(a,b){if(!a)return J;var c=e(a),d=c.path.lastIndexOf("/"),f=c.path.substr(1,d);return b&&k(b,function(a,b){I[a]=b}),I.ignoreErrors.push("Script error."),I.ignoreErrors.push("Script error"),I.ignoreErrors=v(I.ignoreErrors),I.ignoreUrls=I.ignoreUrls.length?v(I.ignoreUrls):!1,I.whitelistUrls=I.whitelistUrls.length?v(I.whitelistUrls):!1,I.includePaths=v(I.includePaths),E=c.user,F=c.path.substr(d+1),C="//"+c.host+(c.port?":"+c.port:"")+"/"+f+"api/"+F+"/store/",c.protocol&&(C=c.protocol+":"+C),I.fetchContext&&(x.remoteFetching=!0),I.linesOfContext&&(x.linesOfContext=I.linesOfContext),x.collectWindowErrors=!!I.collectWindowErrors,J},install:function(){return u()&&x.report.subscribe(m),J},context:function(a,c,d){return g(a)&&(d=c||[],c=a,a=b),J.wrap(a,c).apply(this,d)},wrap:function(a,c){function d(){for(var b=[],d=arguments.length,e=!a||a&&a.deep!==!1;d--;)b[d]=e?J.wrap(a,arguments[d]):arguments[d];try{return c.apply(this,b)}catch(f){throw J.captureException(f,a),f}}if(f(c)&&!g(a))return a;if(g(a)&&(c=a,a=b),!g(c))return c;if(c.__raven__)return c;for(var e in c)c.hasOwnProperty(e)&&(d[e]=c[e]);return d.__raven__=!0,d},uninstall:function(){return x.report.uninstall(),J},captureException:function(a,b){if(h(a))return J.captureMessage(a,b);A=a;try{x.report(a,b)}catch(c){if(a!==c)throw c}return J},captureMessage:function(a,b){return s(q({message:a},b)),J},setUser:function(a){return D=a,J},lastException:function(){return A},lastEventId:function(){return B}},K="source protocol user pass host port path".split(" "),L=/^(?:(\w+):)?\/\/(\w+)(:\w+)?@([\w\.-]+)(?::(\d+))?(\/.*)/;d.prototype=new Error,d.prototype.constructor=d;var M;J.afterLoad(),a.Raven=J,"function"==typeof define&&define.amd&&define("raven",[],function(){return J})}(window);
3/*3//# sourceMappingURL=raven.min.map
4 * Includes TraceKit
5 * https://github.com/getsentry/TraceKit
6 *
7 * Copyright 2013 Matt Robenolt and other contributors
8 * Released under the BSD license
9 * https://github.com/getsentry/raven-js/blob/master/LICENSE
10 *
11 */
12/*
13 TraceKit - Cross brower stack traces - github.com/occ/TraceKit
14 MIT license
15*/
16
17;(function(window, undefined) {
18
19
20var TraceKit = {};
21var _oldTraceKit = window.TraceKit;
22
23// global reference to slice
24var _slice = [].slice;
25var UNKNOWN_FUNCTION = '?';
26
27
28/**
29 * _has, a better form of hasOwnProperty
30 * Example: _has(MainHostObject, property) === true/false
31 *
32 * @param {Object} host object to check property
33 * @param {string} key to check
34 */
35function _has(object, key) {
36 return Object.prototype.hasOwnProperty.call(object, key);
37}
38
39function _isUndefined(what) {
40 return typeof what === 'undefined';
41}
42
43/**
44 * TraceKit.noConflict: Export TraceKit out to another variable
45 * Example: var TK = TraceKit.noConflict()
46 */
47TraceKit.noConflict = function noConflict() {
48 window.TraceKit = _oldTraceKit;
49 return TraceKit;
50};
51
52/**
53 * TraceKit.wrap: Wrap any function in a TraceKit reporter
54 * Example: func = TraceKit.wrap(func);
55 *
56 * @param {Function} func Function to be wrapped
57 * @return {Function} The wrapped func
58 */
59TraceKit.wrap = function traceKitWrapper(func) {
60 function wrapped() {
61 try {
62 return func.apply(this, arguments);
63 } catch (e) {
64 TraceKit.report(e);
65 throw e;
66 }
67 }
68 return wrapped;
69};
70
71/**
72 * TraceKit.report: cross-browser processing of unhandled exceptions
73 *
74 * Syntax:
75 * TraceKit.report.subscribe(function(stackInfo) { ... })
76 * TraceKit.report.unsubscribe(function(stackInfo) { ... })
77 * TraceKit.report(exception)
78 * try { ...code... } catch(ex) { TraceKit.report(ex); }
79 *
80 * Supports:
81 * - Firefox: full stack trace with line numbers, plus column number
82 * on top frame; column number is not guaranteed
83 * - Opera: full stack trace with line and column numbers
84 * - Chrome: full stack trace with line and column numbers
85 * - Safari: line and column number for the top frame only; some frames
86 * may be missing, and column number is not guaranteed
87 * - IE: line and column number for the top frame only; some frames
88 * may be missing, and column number is not guaranteed
89 *
90 * In theory, TraceKit should work on all of the following versions:
91 * - IE5.5+ (only 8.0 tested)
92 * - Firefox 0.9+ (only 3.5+ tested)
93 * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
94 * Exceptions Have Stacktrace to be enabled in opera:config)
95 * - Safari 3+ (only 4+ tested)
96 * - Chrome 1+ (only 5+ tested)
97 * - Konqueror 3.5+ (untested)
98 *
99 * Requires TraceKit.computeStackTrace.
100 *
101 * Tries to catch all unhandled exceptions and report them to the
102 * subscribed handlers. Please note that TraceKit.report will rethrow the
103 * exception. This is REQUIRED in order to get a useful stack trace in IE.
104 * If the exception does not reach the top of the browser, you will only
105 * get a stack trace from the point where TraceKit.report was called.
106 *
107 * Handlers receive a stackInfo object as described in the
108 * TraceKit.computeStackTrace docs.
109 */
110TraceKit.report = (function reportModuleWrapper() {
111 var handlers = [],
112 lastException = null,
113 lastExceptionStack = null;
114
115 /**
116 * Add a crash handler.
117 * @param {Function} handler
118 */
119 function subscribe(handler) {
120 handlers.push(handler);
121 }
122
123 /**
124 * Remove a crash handler.
125 * @param {Function} handler
126 */
127 function unsubscribe(handler) {
128 for (var i = handlers.length - 1; i >= 0; --i) {
129 if (handlers[i] === handler) {
130 handlers.splice(i, 1);
131 }
132 }
133 }
134
135 /**
136 * Dispatch stack information to all handlers.
137 * @param {Object.<string, *>} stack
138 */
139 function notifyHandlers(stack, windowError) {
140 var exception = null;
141 if (windowError && !TraceKit.collectWindowErrors) {
142 return;
143 }
144 for (var i in handlers) {
145 if (_has(handlers, i)) {
146 try {
147 handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
148 } catch (inner) {
149 exception = inner;
150 }
151 }
152 }
153
154 if (exception) {
155 throw exception;
156 }
157 }
158
159 var _oldOnerrorHandler = window.onerror;
160
161 /**
162 * Ensures all global unhandled exceptions are recorded.
163 * Supported by Gecko and IE.
164 * @param {string} message Error message.
165 * @param {string} url URL of script that generated the exception.
166 * @param {(number|string)} lineNo The line number at which the error
167 * occurred.
168 */
169 window.onerror = function traceKitWindowOnError(message, url, lineNo) {
170 var stack = null;
171
172 if (lastExceptionStack) {
173 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
174 stack = lastExceptionStack;
175 lastExceptionStack = null;
176 lastException = null;
177 } else {
178 var location = {
179 'url': url,
180 'line': lineNo
181 };
182 location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
183 location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
184 stack = {
185 'mode': 'onerror',
186 'message': message,
187 'url': document.location.href,
188 'stack': [location],
189 'useragent': navigator.userAgent
190 };
191 }
192
193 notifyHandlers(stack, 'from window.onerror');
194
195 if (_oldOnerrorHandler) {
196 return _oldOnerrorHandler.apply(this, arguments);
197 }
198
199 return false;
200 };
201
202 /**
203 * Reports an unhandled Error to TraceKit.
204 * @param {Error} ex
205 */
206 function report(ex) {
207 var args = _slice.call(arguments, 1);
208 if (lastExceptionStack) {
209 if (lastException === ex) {
210 return; // already caught by an inner catch block, ignore
211 } else {
212 var s = lastExceptionStack;
213 lastExceptionStack = null;
214 lastException = null;
215 notifyHandlers.apply(null, [s, null].concat(args));
216 }
217 }
218
219 var stack = TraceKit.computeStackTrace(ex);
220 lastExceptionStack = stack;
221 lastException = ex;
222
223 // If the stack trace is incomplete, wait for 2 seconds for
224 // slow slow IE to see if onerror occurs or not before reporting
225 // this exception; otherwise, we will end up with an incomplete
226 // stack trace
227 window.setTimeout(function () {
228 if (lastException === ex) {
229 lastExceptionStack = null;
230 lastException = null;
231 notifyHandlers.apply(null, [stack, null].concat(args));
232 }
233 }, (stack.incomplete ? 2000 : 0));
234
235 throw ex; // re-throw to propagate to the top level (and cause window.onerror)
236 }
237
238 report.subscribe = subscribe;
239 report.unsubscribe = unsubscribe;
240 return report;
241}());
242
243/**
244 * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
245 *
246 * Syntax:
247 * s = TraceKit.computeStackTrace.ofCaller([depth])
248 * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
249 * Returns:
250 * s.name - exception name
251 * s.message - exception message
252 * s.stack[i].url - JavaScript or HTML file URL
253 * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)
254 * s.stack[i].args - arguments passed to the function, if known
255 * s.stack[i].line - line number, if known
256 * s.stack[i].column - column number, if known
257 * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#
258 * s.mode - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace
259 *
260 * Supports:
261 * - Firefox: full stack trace with line numbers and unreliable column
262 * number on top frame
263 * - Opera 10: full stack trace with line and column numbers
264 * - Opera 9-: full stack trace with line numbers
265 * - Chrome: full stack trace with line and column numbers
266 * - Safari: line and column number for the topmost stacktrace element
267 * only
268 * - IE: no line numbers whatsoever
269 *
270 * Tries to guess names of anonymous functions by looking for assignments
271 * in the source code. In IE and Safari, we have to guess source file names
272 * by searching for function bodies inside all page scripts. This will not
273 * work for scripts that are loaded cross-domain.
274 * Here be dragons: some function names may be guessed incorrectly, and
275 * duplicate functions may be mismatched.
276 *
277 * TraceKit.computeStackTrace should only be used for tracing purposes.
278 * Logging of unhandled exceptions should be done with TraceKit.report,
279 * which builds on top of TraceKit.computeStackTrace and provides better
280 * IE support by utilizing the window.onerror event to retrieve information
281 * about the top of the stack.
282 *
283 * Note: In IE and Safari, no stack trace is recorded on the Error object,
284 * so computeStackTrace instead walks its *own* chain of callers.
285 * This means that:
286 * * in Safari, some methods may be missing from the stack trace;
287 * * in IE, the topmost function in the stack trace will always be the
288 * caller of computeStackTrace.
289 *
290 * This is okay for tracing (because you are likely to be calling
291 * computeStackTrace from the function you want to be the topmost element
292 * of the stack trace anyway), but not okay for logging unhandled
293 * exceptions (because your catch block will likely be far away from the
294 * inner function that actually caused the exception).
295 *
296 * Tracing example:
297 * function trace(message) {
298 * var stackInfo = TraceKit.computeStackTrace.ofCaller();
299 * var data = message + "\n";
300 * for(var i in stackInfo.stack) {
301 * var item = stackInfo.stack[i];
302 * data += (item.func || '[anonymous]') + "() in " + item.url + ":" + (item.line || '0') + "\n";
303 * }
304 * if (window.console)
305 * console.info(data);
306 * else
307 * alert(data);
308 * }
309 */
310TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
311 var debug = false,
312 sourceCache = {};
313
314 /**
315 * Attempts to retrieve source code via XMLHttpRequest, which is used
316 * to look up anonymous function names.
317 * @param {string} url URL of source code.
318 * @return {string} Source contents.
319 */
320 function loadSource(url) {
321 if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.
322 return '';
323 }
324 try {
325 function getXHR() {
326 try {
327 return new window.XMLHttpRequest();
328 } catch (e) {
329 // explicitly bubble up the exception if not found
330 return new window.ActiveXObject('Microsoft.XMLHTTP');
331 }
332 }
333
334 var request = getXHR();
335 request.open('GET', url, false);
336 request.send('');
337 return request.responseText;
338 } catch (e) {
339 return '';
340 }
341 }
342
343 /**
344 * Retrieves source code from the source code cache.
345 * @param {string} url URL of source code.
346 * @return {Array.<string>} Source contents.
347 */
348 function getSource(url) {
349 if (!_has(sourceCache, url)) {
350 // URL needs to be able to fetched within the acceptable domain. Otherwise,
351 // cross-domain errors will be triggered.
352 var source = '';
353 if (url.indexOf(document.domain) !== -1) {
354 source = loadSource(url);
355 }
356 sourceCache[url] = source ? source.split('\n') : [];
357 }
358
359 return sourceCache[url];
360 }
361
362 /**
363 * Tries to use an externally loaded copy of source code to determine
364 * the name of a function by looking at the name of the variable it was
365 * assigned to, if any.
366 * @param {string} url URL of source code.
367 * @param {(string|number)} lineNo Line number in source code.
368 * @return {string} The function name, if discoverable.
369 */
370 function guessFunctionName(url, lineNo) {
371 var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/,
372 reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,
373 line = '',
374 maxLines = 10,
375 source = getSource(url),
376 m;
377
378 if (!source.length) {
379 return UNKNOWN_FUNCTION;
380 }
381
382 // Walk backwards from the first line in the function until we find the line which
383 // matches the pattern above, which is the function definition
384 for (var i = 0; i < maxLines; ++i) {
385 line = source[lineNo - i] + line;
386
387 if (!_isUndefined(line)) {
388 if ((m = reGuessFunction.exec(line))) {
389 return m[1];
390 } else if ((m = reFunctionArgNames.exec(line))) {
391 return m[1];
392 }
393 }
394 }
395
396 return UNKNOWN_FUNCTION;
397 }
398
399 /**
400 * Retrieves the surrounding lines from where an exception occurred.
401 * @param {string} url URL of source code.
402 * @param {(string|number)} line Line number in source code to centre
403 * around for context.
404 * @return {?Array.<string>} Lines of source code.
405 */
406 function gatherContext(url, line) {
407 var source = getSource(url);
408
409 if (!source.length) {
410 return null;
411 }
412
413 var context = [],
414 // linesBefore & linesAfter are inclusive with the offending line.
415 // if linesOfContext is even, there will be one extra line
416 // *before* the offending line.
417 linesBefore = Math.floor(TraceKit.linesOfContext / 2),
418 // Add one extra line if linesOfContext is odd
419 linesAfter = linesBefore + (TraceKit.linesOfContext % 2),
420 start = Math.max(0, line - linesBefore - 1),
421 end = Math.min(source.length, line + linesAfter - 1);
422
423 line -= 1; // convert to 0-based index
424
425 for (var i = start; i < end; ++i) {
426 if (!_isUndefined(source[i])) {
427 context.push(source[i]);
428 }
429 }
430
431 return context.length > 0 ? context : null;
432 }
433
434 /**
435 * Escapes special characters, except for whitespace, in a string to be
436 * used inside a regular expression as a string literal.
437 * @param {string} text The string.
438 * @return {string} The escaped string literal.
439 */
440 function escapeRegExp(text) {
441 return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
442 }
443
444 /**
445 * Escapes special characters in a string to be used inside a regular
446 * expression as a string literal. Also ensures that HTML entities will
447 * be matched the same as their literal friends.
448 * @param {string} body The string.
449 * @return {string} The escaped string.
450 */
451 function escapeCodeAsRegExpForMatchingInsideHTML(body) {
452 return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('"', '(?:"|&quot;)').replace(/\s+/g, '\\s+');
453 }
454
455 /**
456 * Determines where a code fragment occurs in the source code.
457 * @param {RegExp} re The function definition.
458 * @param {Array.<string>} urls A list of URLs to search.
459 * @return {?Object.<string, (string|number)>} An object containing
460 * the url, line, and column number of the defined function.
461 */
462 function findSourceInUrls(re, urls) {
463 var source, m;
464 for (var i = 0, j = urls.length; i < j; ++i) {
465 // console.log('searching', urls[i]);
466 if ((source = getSource(urls[i])).length) {
467 source = source.join('\n');
468 if ((m = re.exec(source))) {
469 // console.log('Found function in ' + urls[i]);
470
471 return {
472 'url': urls[i],
473 'line': source.substring(0, m.index).split('\n').length,
474 'column': m.index - source.lastIndexOf('\n', m.index) - 1
475 };
476 }
477 }
478 }
479
480 // console.log('no match');
481
482 return null;
483 }
484
485 /**
486 * Determines at which column a code fragment occurs on a line of the
487 * source code.
488 * @param {string} fragment The code fragment.
489 * @param {string} url The URL to search.
490 * @param {(string|number)} line The line number to examine.
491 * @return {?number} The column number.
492 */
493 function findSourceInLine(fragment, url, line) {
494 var source = getSource(url),
495 re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'),
496 m;
497
498 line -= 1;
499
500 if (source && source.length > line && (m = re.exec(source[line]))) {
501 return m.index;
502 }
503
504 return null;
505 }
506
507 /**
508 * Determines where a function was defined within the source code.
509 * @param {(Function|string)} func A function reference or serialized
510 * function definition.
511 * @return {?Object.<string, (string|number)>} An object containing
512 * the url, line, and column number of the defined function.
513 */
514 function findSourceByFunctionBody(func) {
515 var urls = [window.location.href],
516 scripts = document.getElementsByTagName('script'),
517 body,
518 code = '' + func,
519 codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
520 eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
521 re,
522 parts,
523 result;
524
525 for (var i = 0; i < scripts.length; ++i) {
526 var script = scripts[i];
527 if (script.src) {
528 urls.push(script.src);
529 }
530 }
531
532 if (!(parts = codeRE.exec(code))) {
533 re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+'));
534 }
535
536 // not sure if this is really necessary, but I don’t have a test
537 // corpus large enough to confirm that and it was in the original.
538 else {
539 var name = parts[1] ? '\\s+' + parts[1] : '',
540 args = parts[2].split(',').join('\\s*,\\s*');
541
542 body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+');
543 re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}');
544 }
545
546 // look for a normal function definition
547 if ((result = findSourceInUrls(re, urls))) {
548 return result;
549 }
550
551 // look for an old-school event handler function
552 if ((parts = eventRE.exec(code))) {
553 var event = parts[1];
554 body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);
555
556 // look for a function defined in HTML as an onXXX handler
557 re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i');
558
559 if ((result = findSourceInUrls(re, urls[0]))) {
560 return result;
561 }
562
563 // look for ???
564 re = new RegExp(body);
565
566 if ((result = findSourceInUrls(re, urls))) {
567 return result;
568 }
569 }
570
571 return null;
572 }
573
574 // Contents of Exception in various browsers.
575 //
576 // SAFARI:
577 // ex.message = Can't find variable: qq
578 // ex.line = 59
579 // ex.sourceId = 580238192
580 // ex.sourceURL = http://...
581 // ex.expressionBeginOffset = 96
582 // ex.expressionCaretOffset = 98
583 // ex.expressionEndOffset = 98
584 // ex.name = ReferenceError
585 //
586 // FIREFOX:
587 // ex.message = qq is not defined
588 // ex.fileName = http://...
589 // ex.lineNumber = 59
590 // ex.stack = ...stack trace... (see the example below)
591 // ex.name = ReferenceError
592 //
593 // CHROME:
594 // ex.message = qq is not defined
595 // ex.name = ReferenceError
596 // ex.type = not_defined
597 // ex.arguments = ['aa']
598 // ex.stack = ...stack trace...
599 //
600 // INTERNET EXPLORER:
601 // ex.message = ...
602 // ex.name = ReferenceError
603 //
604 // OPERA:
605 // ex.message = ...message... (see the example below)
606 // ex.name = ReferenceError
607 // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
608 // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
609
610 /**
611 * Computes stack trace information from the stack property.
612 * Chrome and Gecko use this property.
613 * @param {Error} ex
614 * @return {?Object.<string, *>} Stack trace information.
615 */
616 function computeStackTraceFromStackProp(ex) {
617 if (!ex.stack) {
618 return null;
619 }
620
621 var chrome = /^\s*at (?:((?:\[object object\])?\S+) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
622 gecko = /^\s*(\S*)(?:\((.*?)\))?@((?:file|http|https).*?):(\d+)(?::(\d+))?\s*$/i,
623 lines = ex.stack.split('\n'),
624 stack = [],
625 parts,
626 element,
627 reference = /^(.*) is undefined$/.exec(ex.message);
628
629 for (var i = 0, j = lines.length; i < j; ++i) {
630 if ((parts = gecko.exec(lines[i]))) {
631 element = {
632 'url': parts[3],
633 'func': parts[1] || UNKNOWN_FUNCTION,
634 'args': parts[2] ? parts[2].split(',') : '',
635 'line': +parts[4],
636 'column': parts[5] ? +parts[5] : null
637 };
638 } else if ((parts = chrome.exec(lines[i]))) {
639 element = {
640 'url': parts[2],
641 'func': parts[1] || UNKNOWN_FUNCTION,
642 'line': +parts[3],
643 'column': parts[4] ? +parts[4] : null
644 };
645 } else {
646 continue;
647 }
648
649 if (!element.func && element.line) {
650 element.func = guessFunctionName(element.url, element.line);
651 }
652
653 if (element.line) {
654 element.context = gatherContext(element.url, element.line);
655 }
656
657 stack.push(element);
658 }
659
660 if (stack[0] && stack[0].line && !stack[0].column && reference) {
661 stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
662 }
663
664 if (!stack.length) {
665 return null;
666 }
667
668 return {
669 'mode': 'stack',
670 'name': ex.name,
671 'message': ex.message,
672 'url': document.location.href,
673 'stack': stack,
674 'useragent': navigator.userAgent
675 };
676 }
677
678 /**
679 * Computes stack trace information from the stacktrace property.
680 * Opera 10 uses this property.
681 * @param {Error} ex
682 * @return {?Object.<string, *>} Stack trace information.
683 */
684 function computeStackTraceFromStacktraceProp(ex) {
685 // Access and store the stacktrace property before doing ANYTHING
686 // else to it because Opera is not very good at providing it
687 // reliably in other circumstances.
688 var stacktrace = ex.stacktrace;
689
690 var testRE = / line (\d+), column (\d+) in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,
691 lines = stacktrace.split('\n'),
692 stack = [],
693 parts;
694
695 for (var i = 0, j = lines.length; i < j; i += 2) {
696 if ((parts = testRE.exec(lines[i]))) {
697 var element = {
698 'line': +parts[1],
699 'column': +parts[2],
700 'func': parts[3] || parts[4],
701 'args': parts[5] ? parts[5].split(',') : [],
702 'url': parts[6]
703 };
704
705 if (!element.func && element.line) {
706 element.func = guessFunctionName(element.url, element.line);
707 }
708 if (element.line) {
709 try {
710 element.context = gatherContext(element.url, element.line);
711 } catch (exc) {}
712 }
713
714 if (!element.context) {
715 element.context = [lines[i + 1]];
716 }
717
718 stack.push(element);
719 }
720 }
721
722 if (!stack.length) {
723 return null;
724 }
725
726 return {
727 'mode': 'stacktrace',
728 'name': ex.name,
729 'message': ex.message,
730 'url': document.location.href,
731 'stack': stack,
732 'useragent': navigator.userAgent
733 };
734 }
735
736 /**
737 * NOT TESTED.
738 * Computes stack trace information from an error message that includes
739 * the stack trace.
740 * Opera 9 and earlier use this method if the option to show stack
741 * traces is turned on in opera:config.
742 * @param {Error} ex
743 * @return {?Object.<string, *>} Stack information.
744 */
745 function computeStackTraceFromOperaMultiLineMessage(ex) {
746 // Opera includes a stack trace into the exception message. An example is:
747 //
748 // Statement on line 3: Undefined variable: undefinedFunc
749 // Backtrace:
750 // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
751 // undefinedFunc(a);
752 // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
753 // zzz(x, y, z);
754 // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
755 // yyy(a, a, a);
756 // Line 1 of function script
757 // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
758 // ...
759
760 var lines = ex.message.split('\n');
761 if (lines.length < 4) {
762 return null;
763 }
764
765 var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|http|https)\S+)(?:: in function (\S+))?\s*$/i,
766 lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|http|https)\S+)(?:: in function (\S+))?\s*$/i,
767 lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
768 stack = [],
769 scripts = document.getElementsByTagName('script'),
770 inlineScriptBlocks = [],
771 parts,
772 i,
773 len,
774 source;
775
776 for (i in scripts) {
777 if (_has(scripts, i) && !scripts[i].src) {
778 inlineScriptBlocks.push(scripts[i]);
779 }
780 }
781
782 for (i = 2, len = lines.length; i < len; i += 2) {
783 var item = null;
784 if ((parts = lineRE1.exec(lines[i]))) {
785 item = {
786 'url': parts[2],
787 'func': parts[3],
788 'line': +parts[1]
789 };
790 } else if ((parts = lineRE2.exec(lines[i]))) {
791 item = {
792 'url': parts[3],
793 'func': parts[4]
794 };
795 var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block
796 var script = inlineScriptBlocks[parts[2] - 1];
797 if (script) {
798 source = getSource(item.url);
799 if (source) {
800 source = source.join('\n');
801 var pos = source.indexOf(script.innerText);
802 if (pos >= 0) {
803 item.line = relativeLine + source.substring(0, pos).split('\n').length;
804 }
805 }
806 }
807 } else if ((parts = lineRE3.exec(lines[i]))) {
808 var url = window.location.href.replace(/#.*$/, ''),
809 line = parts[1];
810 var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[i + 1]));
811 source = findSourceInUrls(re, [url]);
812 item = {
813 'url': url,
814 'line': source ? source.line : line,
815 'func': ''
816 };
817 }
818
819 if (item) {
820 if (!item.func) {
821 item.func = guessFunctionName(item.url, item.line);
822 }
823 var context = gatherContext(item.url, item.line);
824 var midline = (context ? context[Math.floor(context.length / 2)] : null);
825 if (context && midline.replace(/^\s*/, '') === lines[i + 1].replace(/^\s*/, '')) {
826 item.context = context;
827 } else {
828 // if (context) alert("Context mismatch. Correct midline:\n" + lines[i+1] + "\n\nMidline:\n" + midline + "\n\nContext:\n" + context.join("\n") + "\n\nURL:\n" + item.url);
829 item.context = [lines[i + 1]];
830 }
831 stack.push(item);
832 }
833 }
834 if (!stack.length) {
835 return null; // could not parse multiline exception message as Opera stack trace
836 }
837
838 return {
839 'mode': 'multiline',
840 'name': ex.name,
841 'message': lines[0],
842 'url': document.location.href,
843 'stack': stack,
844 'useragent': navigator.userAgent
845 };
846 }
847
848 /**
849 * Adds information about the first frame to incomplete stack traces.
850 * Safari and IE require this to get complete data on the first frame.
851 * @param {Object.<string, *>} stackInfo Stack trace information from
852 * one of the compute* methods.
853 * @param {string} url The URL of the script that caused an error.
854 * @param {(number|string)} lineNo The line number of the script that
855 * caused an error.
856 * @param {string=} message The error generated by the browser, which
857 * hopefully contains the name of the object that caused the error.
858 * @return {boolean} Whether or not the stack information was
859 * augmented.
860 */
861 function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
862 var initial = {
863 'url': url,
864 'line': lineNo
865 };
866
867 if (initial.url && initial.line) {
868 stackInfo.incomplete = false;
869
870 if (!initial.func) {
871 initial.func = guessFunctionName(initial.url, initial.line);
872 }
873
874 if (!initial.context) {
875 initial.context = gatherContext(initial.url, initial.line);
876 }
877
878 var reference = / '([^']+)' /.exec(message);
879 if (reference) {
880 initial.column = findSourceInLine(reference[1], initial.url, initial.line);
881 }
882
883 if (stackInfo.stack.length > 0) {
884 if (stackInfo.stack[0].url === initial.url) {
885 if (stackInfo.stack[0].line === initial.line) {
886 return false; // already in stack trace
887 } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {
888 stackInfo.stack[0].line = initial.line;
889 stackInfo.stack[0].context = initial.context;
890 return false;
891 }
892 }
893 }
894
895 stackInfo.stack.unshift(initial);
896 stackInfo.partial = true;
897 return true;
898 } else {
899 stackInfo.incomplete = true;
900 }
901
902 return false;
903 }
904
905 /**
906 * Computes stack trace information by walking the arguments.caller
907 * chain at the time the exception occurred. This will cause earlier
908 * frames to be missed but is the only way to get any stack trace in
909 * Safari and IE. The top frame is restored by
910 * {@link augmentStackTraceWithInitialElement}.
911 * @param {Error} ex
912 * @return {?Object.<string, *>} Stack trace information.
913 */
914 function computeStackTraceByWalkingCallerChain(ex, depth) {
915 var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,
916 stack = [],
917 funcs = {},
918 recursion = false,
919 parts,
920 item,
921 source;
922
923 for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {
924 if (curr === computeStackTrace || curr === TraceKit.report) {
925 // console.log('skipping internal function');
926 continue;
927 }
928
929 item = {
930 'url': null,
931 'func': UNKNOWN_FUNCTION,
932 'line': null,
933 'column': null
934 };
935
936 if (curr.name) {
937 item.func = curr.name;
938 } else if ((parts = functionName.exec(curr.toString()))) {
939 item.func = parts[1];
940 }
941
942 if ((source = findSourceByFunctionBody(curr))) {
943 item.url = source.url;
944 item.line = source.line;
945
946 if (item.func === UNKNOWN_FUNCTION) {
947 item.func = guessFunctionName(item.url, item.line);
948 }
949
950 var reference = / '([^']+)' /.exec(ex.message || ex.description);
951 if (reference) {
952 item.column = findSourceInLine(reference[1], source.url, source.line);
953 }
954 }
955
956 if (funcs['' + curr]) {
957 recursion = true;
958 }else{
959 funcs['' + curr] = true;
960 }
961
962 stack.push(item);
963 }
964
965 if (depth) {
966 // console.log('depth is ' + depth);
967 // console.log('stack is ' + stack.length);
968 stack.splice(0, depth);
969 }
970
971 var result = {
972 'mode': 'callers',
973 'name': ex.name,
974 'message': ex.message,
975 'url': document.location.href,
976 'stack': stack,
977 'useragent': navigator.userAgent
978 };
979 augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);
980 return result;
981 }
982
983 /**
984 * Computes a stack trace for an exception.
985 * @param {Error} ex
986 * @param {(string|number)=} depth
987 */
988 function computeStackTrace(ex, depth) {
989 var stack = null;
990 depth = (depth == null ? 0 : +depth);
991
992 try {
993 // This must be tried first because Opera 10 *destroys*
994 // its stacktrace property if you try to access the stack
995 // property first!!
996 stack = computeStackTraceFromStacktraceProp(ex);
997 if (stack) {
998 return stack;
999 }
1000 } catch (e) {
1001 if (debug) {
1002 throw e;
1003 }
1004 }
1005
1006 try {
1007 stack = computeStackTraceFromStackProp(ex);
1008 if (stack) {
1009 return stack;
1010 }
1011 } catch (e) {
1012 if (debug) {
1013 throw e;
1014 }
1015 }
1016
1017 try {
1018 stack = computeStackTraceFromOperaMultiLineMessage(ex);
1019 if (stack) {
1020 return stack;
1021 }
1022 } catch (e) {
1023 if (debug) {
1024 throw e;
1025 }
1026 }
1027
1028 try {
1029 stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);
1030 if (stack) {
1031 return stack;
1032 }
1033 } catch (e) {
1034 if (debug) {
1035 throw e;
1036 }
1037 }
1038
1039 return {
1040 'mode': 'failed'
1041 };
1042 }
1043
1044 /**
1045 * Logs a stacktrace starting from the previous call and working down.
1046 * @param {(number|string)=} depth How many frames deep to trace.
1047 * @return {Object.<string, *>} Stack trace information.
1048 */
1049 function computeStackTraceOfCaller(depth) {
1050 depth = (depth == null ? 0 : +depth) + 1; // "+ 1" because "ofCaller" should drop one frame
1051 try {
1052 throw new Error();
1053 } catch (ex) {
1054 return computeStackTrace(ex, depth + 1);
1055 }
1056
1057 return null;
1058 }
1059
1060 computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;
1061 computeStackTrace.guessFunctionName = guessFunctionName;
1062 computeStackTrace.gatherContext = gatherContext;
1063 computeStackTrace.ofCaller = computeStackTraceOfCaller;
1064
1065 return computeStackTrace;
1066}());
1067
1068/**
1069 * Extends support for global error handling for asynchronous browser
1070 * functions. Adopted from Closure Library's errorhandler.js
1071 */
1072(function extendToAsynchronousCallbacks() {
1073 var _helper = function _helper(fnName) {
1074 var originalFn = window[fnName];
1075 window[fnName] = function traceKitAsyncExtension() {
1076 // Make a copy of the arguments
1077 var args = _slice.call(arguments);
1078 var originalCallback = args[0];
1079 if (typeof (originalCallback) === 'function') {
1080 args[0] = TraceKit.wrap(originalCallback);
1081 }
1082 // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
1083 // also only supports 2 argument and doesn't care what "this" is, so we
1084 // can just call the original function directly.
1085 if (originalFn.apply) {
1086 return originalFn.apply(this, args);
1087 } else {
1088 return originalFn(args[0], args[1]);
1089 }
1090 };
1091 };
1092
1093 _helper('setTimeout');
1094 _helper('setInterval');
1095}());
1096
1097//Default options:
1098if (!TraceKit.remoteFetching) {
1099 TraceKit.remoteFetching = true;
1100}
1101if (!TraceKit.collectWindowErrors) {
1102 TraceKit.collectWindowErrors = true;
1103}
1104if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {
1105 // 5 lines before, the offending line, 5 lines after
1106 TraceKit.linesOfContext = 11;
1107}
1108
1109
1110
1111// Export to global object
1112window.TraceKit = TraceKit;
1113
1114}(window));
1115;(function(window, undefined){
1116'use strict';
1117
1118// First, check for JSON support
1119// If there is no JSON, we no-op the core features of Raven
1120// since JSON is required to encode the payload
1121var _Raven = window.Raven,
1122 hasJSON = !isUndefined(window.JSON),
1123 globalServer,
1124 globalUser,
1125 globalKey,
1126 globalProject,
1127 globalOptions = {
1128 logger: 'javascript',
1129 ignoreErrors: [],
1130 ignoreUrls: []
1131 };
1132
1133var TK = TraceKit.noConflict();
1134
1135// Disable Tracekit's remote fetching by default
1136TK.remoteFetching = false;
1137
1138/*
1139 * The core Raven singleton
1140 *
1141 * @this {Raven}
1142 */
1143var Raven = {
1144 VERSION: '1.0.8',
1145
1146 /*
1147 * Allow multiple versions of Raven to be installed.
1148 * Strip Raven from the global context and returns the instance.
1149 *
1150 * @return {Raven}
1151 */
1152 noConflict: function() {
1153 window.Raven = _Raven;
1154 return Raven;
1155 },
1156
1157 /*
1158 * Configure Raven with a DSN and extra options
1159 *
1160 * @param {string} dsn The public Sentry DSN
1161 * @param {object} options Optional set of of global options [optional]
1162 * @return {Raven}
1163 */
1164 config: function(dsn, options) {
1165 var uri = parseUri(dsn),
1166 lastSlash = uri.path.lastIndexOf('/'),
1167 path = uri.path.substr(1, lastSlash);
1168
1169 if (options && options.ignoreErrors && window.console && console.warn) {
1170 console.warn('DeprecationWarning: `ignoreErrors` is going to be removed soon.');
1171 }
1172
1173 // merge in options
1174 if (options) {
1175 each(options, function(key, value){
1176 globalOptions[key] = value;
1177 });
1178 }
1179
1180 // "Script error." is hard coded into browsers for errors that it can't read.
1181 // this is the result of a script being pulled in from an external domain and CORS.
1182 globalOptions.ignoreErrors.push('Script error.');
1183
1184 globalKey = uri.user;
1185 globalProject = ~~uri.path.substr(lastSlash + 1);
1186
1187 // assemble the endpoint from the uri pieces
1188 globalServer = '//' + uri.host +
1189 (uri.port ? ':' + uri.port : '') +
1190 '/' + path + 'api/' + globalProject + '/store/';
1191
1192 if (uri.protocol) {
1193 globalServer = uri.protocol + ':' + globalServer;
1194 }
1195
1196 if (globalOptions.fetchContext) {
1197 TK.remoteFetching = true;
1198 }
1199
1200 // return for chaining
1201 return Raven;
1202 },
1203
1204 /*
1205 * Installs a global window.onerror error handler
1206 * to capture and report uncaught exceptions.
1207 * At this point, install() is required to be called due
1208 * to the way TraceKit is set up.
1209 *
1210 * @return {Raven}
1211 */
1212 install: function() {
1213 if (!isSetup()) return;
1214
1215 TK.report.subscribe(handleStackInfo);
1216
1217 return Raven;
1218 },
1219
1220 /*
1221 * Wrap code within a context so Raven can capture errors
1222 * reliably across domains that is executed immediately.
1223 *
1224 * @param {object} options A specific set of options for this context [optional]
1225 * @param {function} func The callback to be immediately executed within the context
1226 * @param {array} args An array of arguments to be called with the callback [optional]
1227 */
1228 context: function(options, func, args) {
1229 if (isFunction(options)) {
1230 args = func || [];
1231 func = options;
1232 options = undefined;
1233 }
1234
1235 Raven.wrap(options, func).apply(this, args);
1236 },
1237
1238 /*
1239 * Wrap code within a context and returns back a new function to be executed
1240 *
1241 * @param {object} options A specific set of options for this context [optional]
1242 * @param {function} func The function to be wrapped in a new context
1243 * @return {function} The newly wrapped functions with a context
1244 */
1245 wrap: function(options, func) {
1246 // options is optional
1247 if (isFunction(options)) {
1248 func = options;
1249 options = undefined;
1250 }
1251
1252 return function() {
1253 try {
1254 func.apply(this, arguments);
1255 } catch(e) {
1256 Raven.captureException(e, options);
1257 throw e;
1258 }
1259 };
1260 },
1261
1262 /*
1263 * Uninstalls the global error handler.
1264 *
1265 * @return {Raven}
1266 */
1267 uninstall: function() {
1268 TK.report.unsubscribe(handleStackInfo);
1269
1270 return Raven;
1271 },
1272
1273 /*
1274 * Manually capture an exception and send it over to Sentry
1275 *
1276 * @param {error} ex An exception to be logged
1277 * @param {object} options A specific set of options for this error [optional]
1278 * @return {Raven}
1279 */
1280 captureException: function(ex, options) {
1281 // If a string is passed through, recall as a message
1282 if (typeof ex === 'string') {
1283 return Raven.captureMessage(ex, options);
1284 }
1285
1286 // TraceKit.report will re-raise any exception passed to it,
1287 // which means you have to wrap it in try/catch. Instead, we
1288 // can wrap it here and only re-raise if TraceKit.report
1289 // raises an exception different from the one we asked to
1290 // report on.
1291 try {
1292 TK.report(ex, options);
1293 } catch(ex1) {
1294 if(ex !== ex1) {
1295 throw ex1;
1296 }
1297 }
1298
1299 return Raven;
1300 },
1301
1302 /*
1303 * Manually send a message to Sentry
1304 *
1305 * @param {string} msg A plain message to be captured in Sentry
1306 * @param {object} options A specific set of options for this message [optional]
1307 * @return {Raven}
1308 */
1309 captureMessage: function(msg, options) {
1310 // Fire away!
1311 send(
1312 arrayMerge({
1313 message: msg
1314 }, options)
1315 );
1316
1317 return Raven;
1318 },
1319
1320 /*
1321 * Set/clear a user to be sent along with the payload.
1322 *
1323 * @param {object} user An object representing user data [optional]
1324 * @return {Raven}
1325 */
1326 setUser: function(user) {
1327 globalUser = user;
1328
1329 return Raven;
1330 }
1331};
1332
1333var uriKeys = 'source protocol authority userInfo user password host port relative path directory file query anchor'.split(' '),
1334 uriPattern = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
1335
1336/**** Private functions ****/
1337function parseUri(str) {
1338 var m = uriPattern.exec(str),
1339 uri = {},
1340 i = 14;
1341
1342 while (i--) uri[uriKeys[i]] = m[i] || '';
1343
1344 return uri;
1345}
1346
1347function isUndefined(what) {
1348 return typeof what === 'undefined';
1349}
1350
1351function isFunction(what) {
1352 return typeof what === 'function';
1353}
1354
1355function each(obj, callback) {
1356 var i, j;
1357
1358 if (isUndefined(obj.length)) {
1359 for (i in obj) {
1360 if (obj.hasOwnProperty(i)) {
1361 callback.call(null, i, obj[i]);
1362 }
1363 }
1364 } else {
1365 for (i = 0, j = obj.length; i < j; i++) {
1366 callback.call(null, i, obj[i]);
1367 }
1368 }
1369}
1370
1371var cachedAuth;
1372
1373function getAuthQueryString() {
1374 if (cachedAuth) return cachedAuth;
1375
1376 var qs = [
1377 'sentry_version=2.0',
1378 'sentry_client=raven-js/' + Raven.VERSION
1379 ];
1380 if (globalKey) {
1381 qs.push('sentry_key=' + globalKey);
1382 }
1383
1384 cachedAuth = '?' + qs.join('&');
1385 return cachedAuth;
1386}
1387
1388function handleStackInfo(stackInfo, options) {
1389 var frames = [],
1390 i = 0,
1391 j, frame;
1392
1393 if (stackInfo.stack && (j = stackInfo.stack.length)) {
1394 for (; i < j; i++) {
1395 frame = normalizeFrame(stackInfo.stack[i]);
1396 if (frame) {
1397 frames.push(frame);
1398 }
1399 }
1400 }
1401
1402 processException(
1403 stackInfo.name,
1404 stackInfo.message,
1405 stackInfo.url,
1406 stackInfo.lineno,
1407 frames,
1408 options
1409 );
1410}
1411
1412function normalizeFrame(frame) {
1413 if (!frame.url) return;
1414
1415 // normalize the frames data
1416 var normalized = {
1417 filename: frame.url,
1418 lineno: frame.line,
1419 colno: frame.column,
1420 'function': frame.func || '?'
1421 }, context = extractContextFromFrame(frame);
1422
1423 if (context) {
1424 var i = 3, keys = ['pre_context', 'context_line', 'post_context'];
1425 while (i--) normalized[keys[i]] = context[i];
1426 }
1427
1428 normalized.in_app = !/(Raven|TraceKit)\./.test(normalized['function']);
1429
1430 return normalized;
1431}
1432
1433function extractContextFromFrame(frame) {
1434 // immediately check if we should even attempt to parse a context
1435 if (!frame.context || !globalOptions.fetchContext) return;
1436
1437 var context = frame.context,
1438 pivot = ~~(context.length / 2),
1439 i = context.length, isMinified = false;
1440
1441 while (i--) {
1442 // We're making a guess to see if the source is minified or not.
1443 // To do that, we make the assumption if *any* of the lines passed
1444 // in are greater than 300 characters long, we bail.
1445 // Sentry will see that there isn't a context
1446 if (context[i].length > 300) {
1447 isMinified = true;
1448 break;
1449 }
1450 }
1451
1452 if (isMinified) {
1453 // The source is minified and we don't know which column. Fuck it.
1454 if (isUndefined(frame.column)) return;
1455
1456 // If the source is minified and has a frame column
1457 // we take a chunk of the offending line to hopefully shed some light
1458 return [
1459 [], // no pre_context
1460 context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
1461 [] // no post_context
1462 ];
1463 }
1464
1465 return [
1466 context.slice(0, pivot), // pre_context
1467 context[pivot], // context_line
1468 context.slice(pivot + 1) // post_context
1469 ];
1470}
1471
1472function processException(type, message, fileurl, lineno, frames, options) {
1473 var stacktrace, label, i;
1474
1475 // IE8 really doesn't have Array.prototype.indexOf
1476 // Filter out a message that matches our ignore list
1477 i = globalOptions.ignoreErrors.length;
1478 while (i--) {
1479 if (message === globalOptions.ignoreErrors[i]) {
1480 return;
1481 }
1482 }
1483
1484 if (frames && frames.length) {
1485 stacktrace = {frames: frames};
1486 fileurl = fileurl || frames[0].filename;
1487 } else if (fileurl) {
1488 stacktrace = {
1489 frames: [{
1490 filename: fileurl,
1491 lineno: lineno
1492 }]
1493 };
1494 }
1495
1496 i = globalOptions.ignoreUrls.length;
1497 while (i--) {
1498 if (globalOptions.ignoreUrls[i].test(fileurl)) {
1499 return;
1500 }
1501 }
1502
1503 label = lineno ? message + ' at ' + lineno : message;
1504
1505 // Fire away!
1506 send(
1507 arrayMerge({
1508 'sentry.interfaces.Exception': {
1509 type: type,
1510 value: message
1511 },
1512 'sentry.interfaces.Stacktrace': stacktrace,
1513 culprit: fileurl,
1514 message: label
1515 }, options)
1516 );
1517}
1518
1519function arrayMerge(arr1, arr2) {
1520 if (!arr2) {
1521 return arr1;
1522 }
1523 each(arr2, function(key, value){
1524 arr1[key] = value;
1525 });
1526 return arr1;
1527}
1528
1529function getHttpData() {
1530 var http = {
1531 url: window.location.href,
1532 headers: {
1533 'User-Agent': navigator.userAgent
1534 }
1535 };
1536
1537 if (window.document.referrer) {
1538 http.headers.Referer = window.document.referrer;
1539 }
1540
1541 return http;
1542}
1543
1544function send(data) {
1545 if (!isSetup()) return;
1546
1547 data = arrayMerge({
1548 project: globalProject,
1549 logger: globalOptions.logger,
1550 site: globalOptions.site,
1551 platform: 'javascript',
1552 'sentry.interfaces.Http': getHttpData()
1553 }, data);
1554
1555 if (globalUser) data['sentry.interfaces.User'] = globalUser;
1556
1557 if (isFunction(globalOptions.dataCallback)) {
1558 data = globalOptions.dataCallback(data);
1559 }
1560
1561 makeRequest(data);
1562}
1563
1564function makeRequest(data) {
1565 new Image().src = globalServer + getAuthQueryString() + '&sentry_data=' + encodeURIComponent(JSON.stringify(data));
1566}
1567
1568function isSetup() {
1569 if (!hasJSON) return false; // needs JSON support
1570 if (!globalServer) {
1571 if (window.console && console.error) {
1572 console.error("Error: Raven has not been configured.");
1573 }
1574 return false;
1575 }
1576 return true;
1577}
1578
1579// Expose Raven to the world
1580window.Raven = Raven;
1581
1582// AMD
1583if (typeof define === 'function' && define.amd) {
1584 define('raven', [], function() { return Raven; });
1585}
1586
1587})(window);