History

Update Raven.js

Thibaut Courouble committed 10 years ago

Commit d6ffd861 parent 7d433c11 file · +2092 −1712Browse files

Unified · Split

assets/javascripts/vendor/raven.js+2092−1712
@@ -1,2055 +1,2435 @@
1−/*! Raven.js 1.3.0 (768fdca) | github.com/getsentry/raven-js */
1+/*! Raven.js 2.3.0 (b09d766) | github.com/getsentry/raven-js */
22
33 /*
44 * Includes TraceKit
55 * https://github.com/getsentry/TraceKit
66 *
7− * Copyright 2015 Matt Robenolt and other contributors
7+ * Copyright 2016 Matt Robenolt and other contributors
88 * Released under the BSD license
99 * https://github.com/getsentry/raven-js/blob/master/LICENSE
1010 *
1111 */
12−;(function(window, undefined){
12+
13+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Raven = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
1314 'use strict';
1415
15−/*
16− TraceKit - Cross brower stack traces - github.com/occ/TraceKit
17− MIT license
18−*/
16+function RavenConfigError(message) {
17+ this.name = 'RavenConfigError';
18+ this.message = message;
19+}
20+RavenConfigError.prototype = new Error();
21+RavenConfigError.prototype.constructor = RavenConfigError;
1922
20−var TraceKit = {
21− remoteFetching: false,
22− collectWindowErrors: true,
23− // 3 lines before, the offending line, 3 lines after
24− linesOfContext: 7,
25− debug: false
26−};
23+module.exports = RavenConfigError;
2724
28−// global reference to slice
29−var _slice = [].slice;
30−var UNKNOWN_FUNCTION = '?';
25+},{}],2:[function(_dereq_,module,exports){
26+/*global XDomainRequest:false*/
27+'use strict';
3128
29+var TraceKit = _dereq_(5);
30+var RavenConfigError = _dereq_(1);
31+var utils = _dereq_(4);
32+
33+var isFunction = utils.isFunction;
34+var isUndefined = utils.isUndefined;
35+var isError = utils.isError;
36+var isEmptyObject = utils.isEmptyObject;
37+var hasKey = utils.hasKey;
38+var joinRegExp = utils.joinRegExp;
39+var each = utils.each;
40+var objectMerge = utils.objectMerge;
41+var truncate = utils.truncate;
42+var urlencode = utils.urlencode;
43+var uuid4 = utils.uuid4;
3244
33−function getLocationHref() {
34− if (typeof document === 'undefined')
35− return '';
45+var dsnKeys = 'source protocol user pass host port path'.split(' '),
46+ dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
3647
37− return document.location.href;
38−};
48+function now() {
49+ return +new Date();
50+}
3951
40−/**
41− * TraceKit.report: cross-browser processing of unhandled exceptions
42− *
43− * Syntax:
44− * TraceKit.report.subscribe(function(stackInfo) { ... })
45− * TraceKit.report.unsubscribe(function(stackInfo) { ... })
46− * TraceKit.report(exception)
47− * try { ...code... } catch(ex) { TraceKit.report(ex); }
48− *
49− * Supports:
50− * - Firefox: full stack trace with line numbers, plus column number
51− * on top frame; column number is not guaranteed
52− * - Opera: full stack trace with line and column numbers
53− * - Chrome: full stack trace with line and column numbers
54− * - Safari: line and column number for the top frame only; some frames
55− * may be missing, and column number is not guaranteed
56− * - IE: line and column number for the top frame only; some frames
57− * may be missing, and column number is not guaranteed
58− *
59− * In theory, TraceKit should work on all of the following versions:
60− * - IE5.5+ (only 8.0 tested)
61− * - Firefox 0.9+ (only 3.5+ tested)
62− * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
63− * Exceptions Have Stacktrace to be enabled in opera:config)
64− * - Safari 3+ (only 4+ tested)
65− * - Chrome 1+ (only 5+ tested)
66− * - Konqueror 3.5+ (untested)
67− *
68− * Requires TraceKit.computeStackTrace.
69− *
70− * Tries to catch all unhandled exceptions and report them to the
71− * subscribed handlers. Please note that TraceKit.report will rethrow the
72− * exception. This is REQUIRED in order to get a useful stack trace in IE.
73− * If the exception does not reach the top of the browser, you will only
74− * get a stack trace from the point where TraceKit.report was called.
52+// First, check for JSON support
53+// If there is no JSON, we no-op the core features of Raven
54+// since JSON is required to encode the payload
55+function Raven() {
56+ this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
57+ // Raven can run in contexts where there's no document (react-native)
58+ this._hasDocument = typeof document !== 'undefined';
59+ this._lastCapturedException = null;
60+ this._lastEventId = null;
61+ this._globalServer = null;
62+ this._globalKey = null;
63+ this._globalProject = null;
64+ this._globalContext = {};
65+ this._globalOptions = {
66+ logger: 'javascript',
67+ ignoreErrors: [],
68+ ignoreUrls: [],
69+ whitelistUrls: [],
70+ includePaths: [],
71+ crossOrigin: 'anonymous',
72+ collectWindowErrors: true,
73+ maxMessageLength: 0,
74+ stackTraceLimit: 50
75+ };
76+ this._ignoreOnError = 0;
77+ this._isRavenInstalled = false;
78+ this._originalErrorStackTraceLimit = Error.stackTraceLimit;
79+ // capture references to window.console *and* all its methods first
80+ // before the console plugin has a chance to monkey patch
81+ this._originalConsole = window.console || {};
82+ this._originalConsoleMethods = {};
83+ this._plugins = [];
84+ this._startTime = now();
85+ this._wrappedBuiltIns = [];
86+
87+ for (var method in this._originalConsole) { // eslint-disable-line guard-for-in
88+ this._originalConsoleMethods[method] = this._originalConsole[method];
89+ }
90+}
91+
92+/*
93+ * The core Raven singleton
7594 *
76− * Handlers receive a stackInfo object as described in the
77− * TraceKit.computeStackTrace docs.
95+ * @this {Raven}
7896 */
79−TraceKit.report = (function reportModuleWrapper() {
80− var handlers = [],
81− lastArgs = null,
82− lastException = null,
83− lastExceptionStack = null;
8497
85− /**
86− * Add a crash handler.
87− * @param {Function} handler
88− */
89− function subscribe(handler) {
90− installGlobalHandler();
91− handlers.push(handler);
92− }
98+Raven.prototype = {
99+ // Hardcode version string so that raven source can be loaded directly via
100+ // webpack (using a build step causes webpack #1617). Grunt verifies that
101+ // this value matches package.json during build.
102+ // See: https://github.com/getsentry/raven-js/issues/465
103+ VERSION: '2.3.0',
93104
94− /**
95− * Remove a crash handler.
96− * @param {Function} handler
97− */
98− function unsubscribe(handler) {
99− for (var i = handlers.length - 1; i >= 0; --i) {
100− if (handlers[i] === handler) {
101− handlers.splice(i, 1);
102− }
103− }
104− }
105+ debug: false,
105106
106− /**
107− * Remove all crash handlers.
108− */
109− function unsubscribeAll() {
110− uninstallGlobalHandler();
111− handlers = [];
112− }
107+ TraceKit: TraceKit, // alias to TraceKit
113108
114− /**
115− * Dispatch stack information to all handlers.
116− * @param {Object.<string, *>} stack
109+ /*
110+ * Configure Raven with a DSN and extra options
111+ *
112+ * @param {string} dsn The public Sentry DSN
113+ * @param {object} options Optional set of of global options [optional]
114+ * @return {Raven}
117115 */
118− function notifyHandlers(stack, isWindowError) {
119− var exception = null;
120− if (isWindowError && !TraceKit.collectWindowErrors) {
121− return;
116+ config: function(dsn, options) {
117+ var self = this;
118+
119+ if (this._globalServer) {
120+ this._logDebug('error', 'Error: Raven has already been configured');
121+ return this;
122122 }
123− for (var i in handlers) {
124− if (hasKey(handlers, i)) {
125− try {
126− handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
127− } catch (inner) {
128− exception = inner;
123+ if (!dsn) return this;
124+
125+ // merge in options
126+ if (options) {
127+ each(options, function(key, value){
128+ // tags and extra are special and need to be put into context
129+ if (key === 'tags' || key === 'extra') {
130+ self._globalContext[key] = value;
131+ } else {
132+ self._globalOptions[key] = value;
129133 }
130− }
134+ });
131135 }
132136
133− if (exception) {
134− throw exception;
135− }
136− }
137+ var uri = this._parseDSN(dsn),
138+ lastSlash = uri.path.lastIndexOf('/'),
139+ path = uri.path.substr(1, lastSlash);
137140
138− var _oldOnerrorHandler, _onErrorHandlerInstalled;
141+ this._dsn = dsn;
139142
140− /**
141− * Ensures all global unhandled exceptions are recorded.
142− * Supported by Gecko and IE.
143− * @param {string} message Error message.
144− * @param {string} url URL of script that generated the exception.
145− * @param {(number|string)} lineNo The line number at which the error
146− * occurred.
147− * @param {?(number|string)} colNo The column number at which the error
148− * occurred.
149− * @param {?Error} ex The actual Error object.
150− */
151− function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
152− var stack = null;
143+ // "Script error." is hard coded into browsers for errors that it can't read.
144+ // this is the result of a script being pulled in from an external domain and CORS.
145+ this._globalOptions.ignoreErrors.push(/^Script error\.?$/);
146+ this._globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
153147
154− if (lastExceptionStack) {
155− TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
156− processLastException();
157− } else if (ex) {
158− // New chrome and blink send along a real error object
159− // Let's just report that like a normal error.
160− // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
161− stack = TraceKit.computeStackTrace(ex);
162− notifyHandlers(stack, true);
163− } else {
164− var location = {
165− 'url': url,
166− 'line': lineNo,
167− 'column': colNo
168− };
169− location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
170− location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
171− stack = {
172− 'message': message,
173− 'url': getLocationHref(),
174− 'stack': [location]
175− };
176− notifyHandlers(stack, true);
177− }
148+ // join regexp rules into one big rule
149+ this._globalOptions.ignoreErrors = joinRegExp(this._globalOptions.ignoreErrors);
150+ this._globalOptions.ignoreUrls = this._globalOptions.ignoreUrls.length ? joinRegExp(this._globalOptions.ignoreUrls) : false;
151+ this._globalOptions.whitelistUrls = this._globalOptions.whitelistUrls.length ? joinRegExp(this._globalOptions.whitelistUrls) : false;
152+ this._globalOptions.includePaths = joinRegExp(this._globalOptions.includePaths);
178153
179− if (_oldOnerrorHandler) {
180− return _oldOnerrorHandler.apply(this, arguments);
181− }
154+ this._globalKey = uri.user;
155+ this._globalSecret = uri.pass && uri.pass.substr(1);
156+ this._globalProject = uri.path.substr(lastSlash + 1);
182157
183− return false;
184− }
158+ this._globalServer = this._getGlobalServer(uri);
185159
186− function installGlobalHandler ()
187− {
188− if (_onErrorHandlerInstalled) {
189− return;
160+ this._globalEndpoint = this._globalServer +
161+ '/' + path + 'api/' + this._globalProject + '/store/';
162+
163+ if (this._globalOptions.fetchContext) {
164+ TraceKit.remoteFetching = true;
190165 }
191− _oldOnerrorHandler = window.onerror;
192− window.onerror = traceKitWindowOnError;
193− _onErrorHandlerInstalled = true;
194− }
195166
196− function uninstallGlobalHandler ()
197− {
198− if (!_onErrorHandlerInstalled) {
199− return;
167+ if (this._globalOptions.linesOfContext) {
168+ TraceKit.linesOfContext = this._globalOptions.linesOfContext;
200169 }
201− window.onerror = _oldOnerrorHandler;
202− _onErrorHandlerInstalled = false;
203− _oldOnerrorHandler = undefined;
204− }
205170
206− function processLastException() {
207− var _lastExceptionStack = lastExceptionStack,
208− _lastArgs = lastArgs;
209− lastArgs = null;
210− lastExceptionStack = null;
211− lastException = null;
212− notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));
213− }
171+ TraceKit.collectWindowErrors = !!this._globalOptions.collectWindowErrors;
214172
215− /**
216− * Reports an unhandled Error to TraceKit.
217− * @param {Error} ex
218− * @param {?boolean} rethrow If false, do not re-throw the exception.
219− * Only used for window.onerror to not cause an infinite loop of
220− * rethrowing.
173+ // return for chaining
174+ return this;
175+ },
176+
177+ /*
178+ * Installs a global window.onerror error handler
179+ * to capture and report uncaught exceptions.
180+ * At this point, install() is required to be called due
181+ * to the way TraceKit is set up.
182+ *
183+ * @return {Raven}
221184 */
222− function report(ex, rethrow) {
223− var args = _slice.call(arguments, 1);
224− if (lastExceptionStack) {
225− if (lastException === ex) {
226− return; // already caught by an inner catch block, ignore
227− } else {
228− processLastException();
229− }
185+ install: function() {
186+ var self = this;
187+ if (this.isSetup() && !this._isRavenInstalled) {
188+ TraceKit.report.subscribe(function () {
189+ self._handleOnErrorStackInfo.apply(self, arguments);
190+ });
191+ this._wrapBuiltIns();
192+
193+ // Install all of the plugins
194+ this._drainPlugins();
195+
196+ this._isRavenInstalled = true;
230197 }
231198
232− var stack = TraceKit.computeStackTrace(ex);
233− lastExceptionStack = stack;
234− lastException = ex;
235− lastArgs = args;
236−
237− // If the stack trace is incomplete, wait for 2 seconds for
238− // slow slow IE to see if onerror occurs or not before reporting
239− // this exception; otherwise, we will end up with an incomplete
240− // stack trace
241− window.setTimeout(function () {
242− if (lastException === ex) {
243− processLastException();
244− }
245− }, (stack.incomplete ? 2000 : 0));
199+ Error.stackTraceLimit = this._globalOptions.stackTraceLimit;
200+ return this;
201+ },
246202
247− if (rethrow !== false) {
248− throw ex; // re-throw to propagate to the top level (and cause window.onerror)
203+ /*
204+ * Wrap code within a context so Raven can capture errors
205+ * reliably across domains that is executed immediately.
206+ *
207+ * @param {object} options A specific set of options for this context [optional]
208+ * @param {function} func The callback to be immediately executed within the context
209+ * @param {array} args An array of arguments to be called with the callback [optional]
210+ */
211+ context: function(options, func, args) {
212+ if (isFunction(options)) {
213+ args = func || [];
214+ func = options;
215+ options = undefined;
249216 }
250− }
251−
252− report.subscribe = subscribe;
253− report.unsubscribe = unsubscribe;
254− report.uninstall = unsubscribeAll;
255− return report;
256−}());
257217
258−/**
259− * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
260− *
261− * Syntax:
262− * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
263− * Returns:
264− * s.name - exception name
265− * s.message - exception message
266− * s.stack[i].url - JavaScript or HTML file URL
267− * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)
268− * s.stack[i].args - arguments passed to the function, if known
269− * s.stack[i].line - line number, if known
270− * s.stack[i].column - column number, if known
271− * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#
272− *
273− * Supports:
274− * - Firefox: full stack trace with line numbers and unreliable column
275− * number on top frame
276− * - Opera 10: full stack trace with line and column numbers
277− * - Opera 9-: full stack trace with line numbers
278− * - Chrome: full stack trace with line and column numbers
279− * - Safari: line and column number for the topmost stacktrace element
280− * only
281− * - IE: no line numbers whatsoever
282− *
283− * Tries to guess names of anonymous functions by looking for assignments
284− * in the source code. In IE and Safari, we have to guess source file names
285− * by searching for function bodies inside all page scripts. This will not
286− * work for scripts that are loaded cross-domain.
287− * Here be dragons: some function names may be guessed incorrectly, and
288− * duplicate functions may be mismatched.
289− *
290− * TraceKit.computeStackTrace should only be used for tracing purposes.
291− * Logging of unhandled exceptions should be done with TraceKit.report,
292− * which builds on top of TraceKit.computeStackTrace and provides better
293− * IE support by utilizing the window.onerror event to retrieve information
294− * about the top of the stack.
295− *
296− * Note: In IE and Safari, no stack trace is recorded on the Error object,
297− * so computeStackTrace instead walks its *own* chain of callers.
298− * This means that:
299− * * in Safari, some methods may be missing from the stack trace;
300− * * in IE, the topmost function in the stack trace will always be the
301− * caller of computeStackTrace.
302− *
303− * This is okay for tracing (because you are likely to be calling
304− * computeStackTrace from the function you want to be the topmost element
305− * of the stack trace anyway), but not okay for logging unhandled
306− * exceptions (because your catch block will likely be far away from the
307− * inner function that actually caused the exception).
308− *
309− */
310−TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
311− var sourceCache = {};
218+ return this.wrap(options, func).apply(this, args);
219+ },
312220
313− /**
314− * Attempts to retrieve source code via XMLHttpRequest, which is used
315− * to look up anonymous function names.
316− * @param {string} url URL of source code.
317− * @return {string} Source contents.
221+ /*
222+ * Wrap code within a context and returns back a new function to be executed
223+ *
224+ * @param {object} options A specific set of options for this context [optional]
225+ * @param {function} func The function to be wrapped in a new context
226+ * @return {function} The newly wrapped functions with a context
318227 */
319− function loadSource(url) {
320− if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.
321− return '';
228+ wrap: function(options, func) {
229+ var self = this;
230+
231+ // 1 argument has been passed, and it's not a function
232+ // so just return it
233+ if (isUndefined(func) && !isFunction(options)) {
234+ return options;
322235 }
323− try {
324− var getXHR = function() {
325− try {
326− return new window.XMLHttpRequest();
327− } catch (e) {
328− // explicitly bubble up the exception if not found
329− return new window.ActiveXObject('Microsoft.XMLHTTP');
330− }
331− };
332236
333− var request = getXHR();
334− request.open('GET', url, false);
335− request.send('');
336− return request.responseText;
337− } catch (e) {
338− return '';
237+ // options is optional
238+ if (isFunction(options)) {
239+ func = options;
240+ options = undefined;
339241 }
340− }
341242
342− /**
343− * Retrieves source code from the source code cache.
344− * @param {string} url URL of source code.
345− * @return {Array.<string>} Source contents.
346− */
347− function getSource(url) {
348− if (!isString(url)) return [];
349− if (!hasKey(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− var domain = '';
354− try { domain = document.domain; } catch (e) {}
355− if (url.indexOf(domain) !== -1) {
356− source = loadSource(url);
243+ // At this point, we've passed along 2 arguments, and the second one
244+ // is not a function either, so we'll just return the second argument.
245+ if (!isFunction(func)) {
246+ return func;
247+ }
248+
249+ // We don't wanna wrap it twice!
250+ try {
251+ if (func.__raven__) {
252+ return func;
357253 }
358− sourceCache[url] = source ? source.split('\n') : [];
254+ } catch (e) {
255+ // Just accessing the __raven__ prop in some Selenium environments
256+ // can cause a "Permission denied" exception (see raven-js#495).
257+ // Bail on wrapping and return the function as-is (defers to window.onerror).
258+ return func;
359259 }
360260
361− return sourceCache[url];
362− }
261+ // If this has already been wrapped in the past, return that
262+ if (func.__raven_wrapper__ ){
263+ return func.__raven_wrapper__ ;
264+ }
363265
364− /**
365− * Tries to use an externally loaded copy of source code to determine
366− * the name of a function by looking at the name of the variable it was
367− * assigned to, if any.
368− * @param {string} url URL of source code.
369− * @param {(string|number)} lineNo Line number in source code.
370− * @return {string} The function name, if discoverable.
371− */
372− function guessFunctionName(url, lineNo) {
373− var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/,
374− reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,
375− line = '',
376− maxLines = 10,
377− source = getSource(url),
378− m;
266+ function wrapped() {
267+ var args = [], i = arguments.length,
268+ deep = !options || options && options.deep !== false;
269+ // Recursively wrap all of a function's arguments that are
270+ // functions themselves.
379271
380− if (!source.length) {
381− return UNKNOWN_FUNCTION;
382− }
272+ while(i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
383273
384− // Walk backwards from the first line in the function until we find the line which
385− // matches the pattern above, which is the function definition
386− for (var i = 0; i < maxLines; ++i) {
387− line = source[lineNo - i] + line;
274+ try {
275+ return func.apply(this, args);
276+ } catch(e) {
277+ self._ignoreNextOnError();
278+ self.captureException(e, options);
279+ throw e;
280+ }
281+ }
388282
389− if (!isUndefined(line)) {
390− if ((m = reGuessFunction.exec(line))) {
391− return m[1];
392− } else if ((m = reFunctionArgNames.exec(line))) {
393− return m[1];
394− }
283+ // copy over properties of the old function
284+ for (var property in func) {
285+ if (hasKey(func, property)) {
286+ wrapped[property] = func[property];
395287 }
396288 }
289+ func.__raven_wrapper__ = wrapped;
397290
398− return UNKNOWN_FUNCTION;
399− }
291+ wrapped.prototype = func.prototype;
400292
401− /**
402− * Retrieves the surrounding lines from where an exception occurred.
403− * @param {string} url URL of source code.
404− * @param {(string|number)} line Line number in source code to centre
405− * around for context.
406− * @return {?Array.<string>} Lines of source code.
407− */
408− function gatherContext(url, line) {
409− var source = getSource(url);
293+ // Signal that this function has been wrapped already
294+ // for both debugging and to prevent it to being wrapped twice
295+ wrapped.__raven__ = true;
296+ wrapped.__inner__ = func;
410297
411− if (!source.length) {
412− return null;
413− }
298+ return wrapped;
299+ },
414300
415− var context = [],
416− // linesBefore & linesAfter are inclusive with the offending line.
417− // if linesOfContext is even, there will be one extra line
418− // *before* the offending line.
419− linesBefore = Math.floor(TraceKit.linesOfContext / 2),
420− // Add one extra line if linesOfContext is odd
421− linesAfter = linesBefore + (TraceKit.linesOfContext % 2),
422− start = Math.max(0, line - linesBefore - 1),
423− end = Math.min(source.length, line + linesAfter - 1);
301+ /*
302+ * Uninstalls the global error handler.
303+ *
304+ * @return {Raven}
305+ */
306+ uninstall: function() {
307+ TraceKit.report.uninstall();
424308
425− line -= 1; // convert to 0-based index
309+ this._restoreBuiltIns();
426310
427− for (var i = start; i < end; ++i) {
428− if (!isUndefined(source[i])) {
429− context.push(source[i]);
430− }
431− }
311+ Error.stackTraceLimit = this._originalErrorStackTraceLimit;
312+ this._isRavenInstalled = false;
432313
433− return context.length > 0 ? context : null;
434− }
314+ return this;
315+ },
435316
436− /**
437− * Escapes special characters, except for whitespace, in a string to be
438− * used inside a regular expression as a string literal.
439− * @param {string} text The string.
440− * @return {string} The escaped string literal.
441− */
442− function escapeRegExp(text) {
443− return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
444− }
445−
446− /**
447− * Escapes special characters in a string to be used inside a regular
448− * expression as a string literal. Also ensures that HTML entities will
449− * be matched the same as their literal friends.
450− * @param {string} body The string.
451− * @return {string} The escaped string.
317+ /*
318+ * Manually capture an exception and send it over to Sentry
319+ *
320+ * @param {error} ex An exception to be logged
321+ * @param {object} options A specific set of options for this error [optional]
322+ * @return {Raven}
452323 */
453− function escapeCodeAsRegExpForMatchingInsideHTML(body) {
454− return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('"', '(?:"|&quot;)').replace(/\s+/g, '\\s+');
455− }
324+ captureException: function(ex, options) {
325+ // If not an Error is passed through, recall as a message instead
326+ if (!isError(ex)) return this.captureMessage(ex, options);
456327
457− /**
458− * Determines where a code fragment occurs in the source code.
459− * @param {RegExp} re The function definition.
460− * @param {Array.<string>} urls A list of URLs to search.
461− * @return {?Object.<string, (string|number)>} An object containing
462− * the url, line, and column number of the defined function.
463− */
464− function findSourceInUrls(re, urls) {
465− var source, m;
466− for (var i = 0, j = urls.length; i < j; ++i) {
467− // console.log('searching', urls[i]);
468− if ((source = getSource(urls[i])).length) {
469− source = source.join('\n');
470− if ((m = re.exec(source))) {
471− // console.log('Found function in ' + urls[i]);
328+ // Store the raw exception object for potential debugging and introspection
329+ this._lastCapturedException = ex;
472330
473− return {
474− 'url': urls[i],
475− 'line': source.substring(0, m.index).split('\n').length,
476− 'column': m.index - source.lastIndexOf('\n', m.index) - 1
477− };
478− }
331+ // TraceKit.report will re-raise any exception passed to it,
332+ // which means you have to wrap it in try/catch. Instead, we
333+ // can wrap it here and only re-raise if TraceKit.report
334+ // raises an exception different from the one we asked to
335+ // report on.
336+ try {
337+ var stack = TraceKit.computeStackTrace(ex);
338+ this._handleStackInfo(stack, options);
339+ } catch(ex1) {
340+ if(ex !== ex1) {
341+ throw ex1;
479342 }
480343 }
481344
482− // console.log('no match');
483−
484− return null;
485− }
345+ return this;
346+ },
486347
487− /**
488− * Determines at which column a code fragment occurs on a line of the
489− * source code.
490− * @param {string} fragment The code fragment.
491− * @param {string} url The URL to search.
492− * @param {(string|number)} line The line number to examine.
493− * @return {?number} The column number.
348+ /*
349+ * Manually send a message to Sentry
350+ *
351+ * @param {string} msg A plain message to be captured in Sentry
352+ * @param {object} options A specific set of options for this message [optional]
353+ * @return {Raven}
494354 */
495− function findSourceInLine(fragment, url, line) {
496− var source = getSource(url),
497− re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'),
498− m;
355+ captureMessage: function(msg, options) {
356+ // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
357+ // early call; we'll error on the side of logging anything called before configuration since it's
358+ // probably something you should see:
359+ if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(msg)) {
360+ return;
361+ }
499362
500− line -= 1;
363+ // Fire away!
364+ this._send(
365+ objectMerge({
366+ message: msg + '' // Make sure it's actually a string
367+ }, options)
368+ );
501369
502− if (source && source.length > line && (m = re.exec(source[line]))) {
503− return m.index;
370+ return this;
371+ },
372+
373+ addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
374+ var pluginArgs = Array.prototype.slice.call(arguments, 1);
375+
376+ this._plugins.push([plugin, pluginArgs]);
377+ if (this._isRavenInstalled) {
378+ this._drainPlugins();
504379 }
505380
506− return null;
507− }
381+ return this;
382+ },
383+
384+ /*
385+ * Set/clear a user to be sent along with the payload.
386+ *
387+ * @param {object} user An object representing user data [optional]
388+ * @return {Raven}
389+ */
390+ setUserContext: function(user) {
391+ // Intentionally do not merge here since that's an unexpected behavior.
392+ this._globalContext.user = user;
393+
394+ return this;
395+ },
396+
397+ /*
398+ * Merge extra attributes to be sent along with the payload.
399+ *
400+ * @param {object} extra An object representing extra data [optional]
401+ * @return {Raven}
402+ */
403+ setExtraContext: function(extra) {
404+ this._mergeContext('extra', extra);
405+
406+ return this;
407+ },
408+
409+ /*
410+ * Merge tags to be sent along with the payload.
411+ *
412+ * @param {object} tags An object representing tags [optional]
413+ * @return {Raven}
414+ */
415+ setTagsContext: function(tags) {
416+ this._mergeContext('tags', tags);
417+
418+ return this;
419+ },
420+
421+ /*
422+ * Clear all of the context.
423+ *
424+ * @return {Raven}
425+ */
426+ clearContext: function() {
427+ this._globalContext = {};
428+
429+ return this;
430+ },
431+
432+ /*
433+ * Get a copy of the current context. This cannot be mutated.
434+ *
435+ * @return {object} copy of context
436+ */
437+ getContext: function() {
438+ // lol javascript
439+ return JSON.parse(JSON.stringify(this._globalContext));
440+ },
441+
442+ /*
443+ * Set release version of application
444+ *
445+ * @param {string} release Typically something like a git SHA to identify version
446+ * @return {Raven}
447+ */
448+ setRelease: function(release) {
449+ this._globalOptions.release = release;
450+
451+ return this;
452+ },
453+
454+ /*
455+ * Set the dataCallback option
456+ *
457+ * @param {function} callback The callback to run which allows the
458+ * data blob to be mutated before sending
459+ * @return {Raven}
460+ */
461+ setDataCallback: function(callback) {
462+ this._globalOptions.dataCallback = callback;
463+
464+ return this;
465+ },
466+
467+ /*
468+ * Set the shouldSendCallback option
469+ *
470+ * @param {function} callback The callback to run which allows
471+ * introspecting the blob before sending
472+ * @return {Raven}
473+ */
474+ setShouldSendCallback: function(callback) {
475+ this._globalOptions.shouldSendCallback = callback;
476+
477+ return this;
478+ },
508479
509480 /**
510− * Determines where a function was defined within the source code.
511− * @param {(Function|string)} func A function reference or serialized
512− * function definition.
513− * @return {?Object.<string, (string|number)>} An object containing
514− * the url, line, and column number of the defined function.
481+ * Override the default HTTP transport mechanism that transmits data
482+ * to the Sentry server.
483+ *
484+ * @param {function} transport Function invoked instead of the default
485+ * `makeRequest` handler.
486+ *
487+ * @return {Raven}
515488 */
516− function findSourceByFunctionBody(func) {
517− if (typeof document === 'undefined')
518− return;
489+ setTransport: function(transport) {
490+ this._globalOptions.transport = transport;
519491
520− var urls = [window.location.href],
521− scripts = document.getElementsByTagName('script'),
522− body,
523− code = '' + func,
524− codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
525− eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
526− re,
527− parts,
528− result;
492+ return this;
493+ },
529494
530− for (var i = 0; i < scripts.length; ++i) {
531− var script = scripts[i];
532− if (script.src) {
533− urls.push(script.src);
495+ /*
496+ * Get the latest raw exception that was captured by Raven.
497+ *
498+ * @return {error}
499+ */
500+ lastException: function() {
501+ return this._lastCapturedException;
502+ },
503+
504+ /*
505+ * Get the last event id
506+ *
507+ * @return {string}
508+ */
509+ lastEventId: function() {
510+ return this._lastEventId;
511+ },
512+
513+ /*
514+ * Determine if Raven is setup and ready to go.
515+ *
516+ * @return {boolean}
517+ */
518+ isSetup: function() {
519+ if (!this._hasJSON) return false; // needs JSON support
520+ if (!this._globalServer) {
521+ if (!this.ravenNotConfiguredError) {
522+ this.ravenNotConfiguredError = true;
523+ this._logDebug('error', 'Error: Raven has not been configured.');
534524 }
525+ return false;
535526 }
527+ return true;
528+ },
536529
537− if (!(parts = codeRE.exec(code))) {
538− re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+'));
530+ afterLoad: function () {
531+ // TODO: remove window dependence?
532+
533+ // Attempt to initialize Raven on load
534+ var RavenConfig = window.RavenConfig;
535+ if (RavenConfig) {
536+ this.config(RavenConfig.dsn, RavenConfig.config).install();
539537 }
538+ },
540539
541− // not sure if this is really necessary, but I don’t have a test
542− // corpus large enough to confirm that and it was in the original.
543− else {
544− var name = parts[1] ? '\\s+' + parts[1] : '',
545− args = parts[2].split(',').join('\\s*,\\s*');
540+ showReportDialog: function (options) {
541+ if (!window.document) // doesn't work without a document (React native)
542+ return;
546543
547− body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+');
548− re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}');
544+ options = options || {};
545+
546+ var lastEventId = options.eventId || this.lastEventId();
547+ if (!lastEventId) {
548+ throw new RavenConfigError('Missing eventId');
549549 }
550550
551− // look for a normal function definition
552− if ((result = findSourceInUrls(re, urls))) {
553− return result;
551+ var dsn = options.dsn || this._dsn;
552+ if (!dsn) {
553+ throw new RavenConfigError('Missing DSN');
554554 }
555555
556− // look for an old-school event handler function
557− if ((parts = eventRE.exec(code))) {
558− var event = parts[1];
559− body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);
556+ var encode = encodeURIComponent;
557+ var qs = '';
558+ qs += '?eventId=' + encode(lastEventId);
559+ qs += '&dsn=' + encode(dsn);
560560
561− // look for a function defined in HTML as an onXXX handler
562− re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i');
561+ var user = options.user || this._globalContext.user;
562+ if (user) {
563+ if (user.name) qs += '&name=' + encode(user.name);
564+ if (user.email) qs += '&email=' + encode(user.email);
565+ }
563566
564− if ((result = findSourceInUrls(re, urls[0]))) {
565− return result;
566− }
567+ var globalServer = this._getGlobalServer(this._parseDSN(dsn));
567568
568− // look for ???
569− re = new RegExp(body);
569+ var script = document.createElement('script');
570+ script.async = true;
571+ script.src = globalServer + '/api/embed/error-page/' + qs;
572+ (document.head || document.body).appendChild(script);
573+ },
570574
571− if ((result = findSourceInUrls(re, urls))) {
572− return result;
573− }
575+ /**** Private functions ****/
576+ _ignoreNextOnError: function () {
577+ var self = this;
578+ this._ignoreOnError += 1;
579+ setTimeout(function () {
580+ // onerror should trigger before setTimeout
581+ self._ignoreOnError -= 1;
582+ });
583+ },
584+
585+ _triggerEvent: function(eventType, options) {
586+ // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
587+ var evt, key;
588+
589+ if (!this._hasDocument)
590+ return;
591+
592+ options = options || {};
593+
594+ eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
595+
596+ if (document.createEvent) {
597+ evt = document.createEvent('HTMLEvents');
598+ evt.initEvent(eventType, true, true);
599+ } else {
600+ evt = document.createEventObject();
601+ evt.eventType = eventType;
574602 }
575603
576− return null;
577− }
604+ for (key in options) if (hasKey(options, key)) {
605+ evt[key] = options[key];
606+ }
578607
579− // Contents of Exception in various browsers.
580− //
581− // SAFARI:
582− // ex.message = Can't find variable: qq
583− // ex.line = 59
584− // ex.sourceId = 580238192
585− // ex.sourceURL = http://...
586− // ex.expressionBeginOffset = 96
587− // ex.expressionCaretOffset = 98
588− // ex.expressionEndOffset = 98
589− // ex.name = ReferenceError
590− //
591− // FIREFOX:
592− // ex.message = qq is not defined
593− // ex.fileName = http://...
594− // ex.lineNumber = 59
595− // ex.columnNumber = 69
596− // ex.stack = ...stack trace... (see the example below)
597− // ex.name = ReferenceError
598− //
599− // CHROME:
600− // ex.message = qq is not defined
601− // ex.name = ReferenceError
602− // ex.type = not_defined
603− // ex.arguments = ['aa']
604− // ex.stack = ...stack trace...
605− //
606− // INTERNET EXPLORER:
607− // ex.message = ...
608− // ex.name = ReferenceError
609− //
610− // OPERA:
611− // ex.message = ...message... (see the example below)
612− // ex.name = ReferenceError
613− // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
614− // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
608+ if (document.createEvent) {
609+ // IE9 if standards
610+ document.dispatchEvent(evt);
611+ } else {
612+ // IE8 regardless of Quirks or Standards
613+ // IE9 if quirks
614+ try {
615+ document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
616+ } catch(e) {
617+ // Do nothing
618+ }
619+ }
620+ },
615621
616622 /**
617− * Computes stack trace information from the stack property.
618− * Chrome and Gecko use this property.
619− * @param {Error} ex
620− * @return {?Object.<string, *>} Stack trace information.
623+ * Install any queued plugins
621624 */
622− function computeStackTraceFromStackProp(ex) {
623− if (isUndefined(ex.stack) || !ex.stack) return;
625+ _wrapBuiltIns: function() {
626+ var self = this;
627+
628+ function fill(obj, name, replacement, noUndo) {
629+ var orig = obj[name];
630+ obj[name] = replacement(orig);
631+ if (!noUndo) {
632+ self._wrappedBuiltIns.push([obj, name, orig]);
633+ }
634+ }
624635
625− var chrome = /^\s*at (.*?) ?\(?((?:(?:file|https?|chrome-extension):.*?)|<anonymous>):(\d+)(?::(\d+))?\)?\s*$/i,
626− gecko = /^\s*(.*?)(?:\((.*?)\))?@((?:file|https?|chrome).*?):(\d+)(?::(\d+))?\s*$/i,
627− winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
628− lines = ex.stack.split('\n'),
629− stack = [],
630− parts,
631− element,
632− reference = /^(.*) is undefined$/.exec(ex.message);
636+ function wrapTimeFn(orig) {
637+ return function (fn, t) { // preserve arity
638+ // Make a copy of the arguments
639+ var args = [].slice.call(arguments);
640+ var originalCallback = args[0];
641+ if (isFunction(originalCallback)) {
642+ args[0] = self.wrap(originalCallback);
643+ }
633644
634− for (var i = 0, j = lines.length; i < j; ++i) {
635− if ((parts = gecko.exec(lines[i]))) {
636− element = {
637− 'url': parts[3],
638− 'func': parts[1] || UNKNOWN_FUNCTION,
639− 'args': parts[2] ? parts[2].split(',') : '',
640− 'line': +parts[4],
641− 'column': parts[5] ? +parts[5] : null
645+ // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
646+ // also supports only two arguments and doesn't care what this is, so we
647+ // can just call the original function directly.
648+ if (orig.apply) {
649+ return orig.apply(this, args);
650+ } else {
651+ return orig(args[0], args[1]);
652+ }
653+ };
654+ }
655+
656+ fill(window, 'setTimeout', wrapTimeFn);
657+ fill(window, 'setInterval', wrapTimeFn);
658+ if (window.requestAnimationFrame) {
659+ fill(window, 'requestAnimationFrame', function (orig) {
660+ return function (cb) {
661+ return orig(self.wrap(cb));
642662 };
643− } else if ((parts = chrome.exec(lines[i]))) {
644− element = {
645− 'url': parts[2],
646− 'func': parts[1] || UNKNOWN_FUNCTION,
647− 'line': +parts[3],
648− 'column': parts[4] ? +parts[4] : null
663+ });
664+ }
665+
666+ // event targets borrowed from bugsnag-js:
667+ // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
668+ 'EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload'.replace(/\w+/g, function (global) {
669+ var proto = window[global] && window[global].prototype;
670+ if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) {
671+ fill(proto, 'addEventListener', function(orig) {
672+ return function (evt, fn, capture, secure) { // preserve arity
673+ try {
674+ if (fn && fn.handleEvent) {
675+ fn.handleEvent = self.wrap(fn.handleEvent);
676+ }
677+ } catch (err) {
678+ // can sometimes get 'Permission denied to access property "handle Event'
679+ }
680+ return orig.call(this, evt, self.wrap(fn), capture, secure);
681+ };
682+ });
683+ fill(proto, 'removeEventListener', function (orig) {
684+ return function (evt, fn, capture, secure) {
685+ fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn);
686+ return orig.call(this, evt, fn, capture, secure);
687+ };
688+ });
689+ }
690+ });
691+
692+ if ('XMLHttpRequest' in window) {
693+ fill(XMLHttpRequest.prototype, 'send', function(origSend) {
694+ return function (data) { // preserve arity
695+ var xhr = this;
696+ 'onreadystatechange onload onerror onprogress'.replace(/\w+/g, function (prop) {
697+ if (prop in xhr && Object.prototype.toString.call(xhr[prop]) === '[object Function]') {
698+ fill(xhr, prop, function (orig) {
699+ return self.wrap(orig);
700+ }, true /* noUndo */); // don't track filled methods on XHR instances
701+ }
702+ });
703+ return origSend.apply(this, arguments);
649704 };
650− } else if ((parts = winjs.exec(lines[i]))) {
651− element = {
652− 'url': parts[2],
653− 'func': parts[1] || UNKNOWN_FUNCTION,
654− 'line': +parts[3],
655− 'column': parts[4] ? +parts[4] : null
705+ });
706+ }
707+
708+ var $ = window.jQuery || window.$;
709+ if ($ && $.fn && $.fn.ready) {
710+ fill($.fn, 'ready', function (orig) {
711+ return function (fn) {
712+ return orig.call(this, self.wrap(fn));
656713 };
657− } else {
658− continue;
659− }
714+ });
715+ }
716+ },
660717
661− if (!element.func && element.line) {
662− element.func = guessFunctionName(element.url, element.line);
663− }
718+ _restoreBuiltIns: function () {
719+ // restore any wrapped builtins
720+ var builtin;
721+ while (this._wrappedBuiltIns.length) {
722+ builtin = this._wrappedBuiltIns.shift();
664723
665− if (element.line) {
666− element.context = gatherContext(element.url, element.line);
667− }
724+ var obj = builtin[0],
725+ name = builtin[1],
726+ orig = builtin[2];
668727
669− stack.push(element);
728+ obj[name] = orig;
670729 }
730+ },
671731
672− if (!stack.length) {
673− return null;
732+ _drainPlugins: function() {
733+ var self = this;
734+
735+ // FIX ME TODO
736+ each(this._plugins, function(_, plugin) {
737+ var installer = plugin[0];
738+ var args = plugin[1];
739+ installer.apply(self, [self].concat(args));
740+ });
741+ },
742+
743+ _parseDSN: function(str) {
744+ var m = dsnPattern.exec(str),
745+ dsn = {},
746+ i = 7;
747+
748+ try {
749+ while (i--) dsn[dsnKeys[i]] = m[i] || '';
750+ } catch(e) {
751+ throw new RavenConfigError('Invalid DSN: ' + str);
674752 }
675753
676− if (stack[0].line && !stack[0].column && reference) {
677− stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
678− } else if (!stack[0].column && !isUndefined(ex.columnNumber)) {
679− // FireFox uses this awesome columnNumber property for its top frame
680− // Also note, Firefox's column number is 0-based and everything else expects 1-based,
681− // so adding 1
682− stack[0].column = ex.columnNumber + 1;
754+ if (dsn.pass && !this._globalOptions.allowSecretKey) {
755+ throw new RavenConfigError('Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key');
683756 }
684757
685− return {
686− 'name': ex.name,
687− 'message': ex.message,
688− 'url': getLocationHref(),
689− 'stack': stack
690− };
691− }
758+ return dsn;
759+ },
692760
693− /**
694− * Computes stack trace information from the stacktrace property.
695− * Opera 10 uses this property.
696− * @param {Error} ex
697− * @return {?Object.<string, *>} Stack trace information.
698− */
699− function computeStackTraceFromStacktraceProp(ex) {
700− // Access and store the stacktrace property before doing ANYTHING
701− // else to it because Opera is not very good at providing it
702− // reliably in other circumstances.
703− var stacktrace = ex.stacktrace;
704− if (isUndefined(ex.stacktrace) || !ex.stacktrace) return;
761+ _getGlobalServer: function(uri) {
762+ // assemble the endpoint from the uri pieces
763+ var globalServer = '//' + uri.host +
764+ (uri.port ? ':' + uri.port : '');
705765
706− var testRE = / line (\d+), column (\d+) in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,
707− lines = stacktrace.split('\n'),
708− stack = [],
709− parts;
766+ if (uri.protocol) {
767+ globalServer = uri.protocol + ':' + globalServer;
768+ }
769+ return globalServer;
770+ },
710771
711− for (var i = 0, j = lines.length; i < j; i += 2) {
712− if ((parts = testRE.exec(lines[i]))) {
713− var element = {
714− 'line': +parts[1],
715− 'column': +parts[2],
716− 'func': parts[3] || parts[4],
717− 'args': parts[5] ? parts[5].split(',') : [],
718− 'url': parts[6]
719− };
772+ _handleOnErrorStackInfo: function() {
773+ // if we are intentionally ignoring errors via onerror, bail out
774+ if (!this._ignoreOnError) {
775+ this._handleStackInfo.apply(this, arguments);
776+ }
777+ },
720778
721− if (!element.func && element.line) {
722− element.func = guessFunctionName(element.url, element.line);
723− }
724− if (element.line) {
725− try {
726− element.context = gatherContext(element.url, element.line);
727− } catch (exc) {}
728− }
779+ _handleStackInfo: function(stackInfo, options) {
780+ var self = this;
781+ var frames = [];
729782
730− if (!element.context) {
731− element.context = [lines[i + 1]];
783+ if (stackInfo.stack && stackInfo.stack.length) {
784+ each(stackInfo.stack, function(i, stack) {
785+ var frame = self._normalizeFrame(stack);
786+ if (frame) {
787+ frames.push(frame);
732788 }
789+ });
790+ }
733791
734− stack.push(element);
792+ this._triggerEvent('handle', {
793+ stackInfo: stackInfo,
794+ options: options
795+ });
796+
797+ this._processException(
798+ stackInfo.name,
799+ stackInfo.message,
800+ stackInfo.url,
801+ stackInfo.lineno,
802+ frames.slice(0, this._globalOptions.stackTraceLimit),
803+ options
804+ );
805+ },
806+
807+ _normalizeFrame: function(frame) {
808+ if (!frame.url) return;
809+
810+ // normalize the frames data
811+ var normalized = {
812+ filename: frame.url,
813+ lineno: frame.line,
814+ colno: frame.column,
815+ 'function': frame.func || '?'
816+ }, context = this._extractContextFromFrame(frame), i;
817+
818+ if (context) {
819+ var keys = ['pre_context', 'context_line', 'post_context'];
820+ i = 3;
821+ while (i--) normalized[keys[i]] = context[i];
822+ }
823+
824+ normalized.in_app = !( // determine if an exception came from outside of our app
825+ // first we check the global includePaths list.
826+ !!this._globalOptions.includePaths.test && !this._globalOptions.includePaths.test(normalized.filename) ||
827+ // Now we check for fun, if the function name is Raven or TraceKit
828+ /(Raven|TraceKit)\./.test(normalized['function']) ||
829+ // finally, we do a last ditch effort and check for raven.min.js
830+ /raven\.(min\.)?js$/.test(normalized.filename)
831+ );
832+
833+ return normalized;
834+ },
835+
836+ _extractContextFromFrame: function(frame) {
837+ // immediately check if we should even attempt to parse a context
838+ if (!frame.context || !this._globalOptions.fetchContext) return;
839+
840+ var context = frame.context,
841+ pivot = ~~(context.length / 2),
842+ i = context.length, isMinified = false;
843+
844+ while (i--) {
845+ // We're making a guess to see if the source is minified or not.
846+ // To do that, we make the assumption if *any* of the lines passed
847+ // in are greater than 300 characters long, we bail.
848+ // Sentry will see that there isn't a context
849+ if (context[i].length > 300) {
850+ isMinified = true;
851+ break;
735852 }
736853 }
737854
738− if (!stack.length) {
739− return null;
855+ if (isMinified) {
856+ // The source is minified and we don't know which column. Fuck it.
857+ if (isUndefined(frame.column)) return;
858+
859+ // If the source is minified and has a frame column
860+ // we take a chunk of the offending line to hopefully shed some light
861+ return [
862+ [], // no pre_context
863+ context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
864+ [] // no post_context
865+ ];
740866 }
741867
742− return {
743− 'name': ex.name,
744− 'message': ex.message,
745− 'url': getLocationHref(),
746− 'stack': stack
747− };
748− }
868+ return [
869+ context.slice(0, pivot), // pre_context
870+ context[pivot], // context_line
871+ context.slice(pivot + 1) // post_context
872+ ];
873+ },
749874
750− /**
751− * NOT TESTED.
752− * Computes stack trace information from an error message that includes
753− * the stack trace.
754− * Opera 9 and earlier use this method if the option to show stack
755− * traces is turned on in opera:config.
756− * @param {Error} ex
757− * @return {?Object.<string, *>} Stack information.
758− */
759− function computeStackTraceFromOperaMultiLineMessage(ex) {
760− // Opera includes a stack trace into the exception message. An example is:
761− //
762− // Statement on line 3: Undefined variable: undefinedFunc
763− // Backtrace:
764− // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
765− // undefinedFunc(a);
766− // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
767− // zzz(x, y, z);
768− // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
769− // yyy(a, a, a);
770− // Line 1 of function script
771− // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
772− // ...
875+ _processException: function(type, message, fileurl, lineno, frames, options) {
876+ var stacktrace, fullMessage;
877+
878+ if (!!this._globalOptions.ignoreErrors.test && this._globalOptions.ignoreErrors.test(message)) return;
879+
880+ message += '';
881+ message = truncate(message, this._globalOptions.maxMessageLength);
882+
883+ fullMessage = (type ? type + ': ' : '') + message;
884+ fullMessage = truncate(fullMessage, this._globalOptions.maxMessageLength);
885+
886+ if (frames && frames.length) {
887+ fileurl = frames[0].filename || fileurl;
888+ // Sentry expects frames oldest to newest
889+ // and JS sends them as newest to oldest
890+ frames.reverse();
891+ stacktrace = {frames: frames};
892+ } else if (fileurl) {
893+ stacktrace = {
894+ frames: [{
895+ filename: fileurl,
896+ lineno: lineno,
897+ in_app: true
898+ }]
899+ };
900+ }
773901
774− var lines = ex.message.split('\n');
775− if (lines.length < 4) {
776− return null;
902+ if (!!this._globalOptions.ignoreUrls.test && this._globalOptions.ignoreUrls.test(fileurl)) return;
903+ if (!!this._globalOptions.whitelistUrls.test && !this._globalOptions.whitelistUrls.test(fileurl)) return;
904+
905+ var data = objectMerge({
906+ // sentry.interfaces.Exception
907+ exception: {
908+ values: [{
909+ type: type,
910+ value: message,
911+ stacktrace: stacktrace
912+ }]
913+ },
914+ culprit: fileurl,
915+ message: fullMessage
916+ }, options);
917+
918+ // Fire away!
919+ this._send(data);
920+ },
921+
922+ _trimPacket: function(data) {
923+ // For now, we only want to truncate the two different messages
924+ // but this could/should be expanded to just trim everything
925+ var max = this._globalOptions.maxMessageLength;
926+ data.message = truncate(data.message, max);
927+ if (data.exception) {
928+ var exception = data.exception.values[0];
929+ exception.value = truncate(exception.value, max);
777930 }
778931
779− var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
780− lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
781− lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
782− stack = [],
783− scripts = document.getElementsByTagName('script'),
784− inlineScriptBlocks = [],
785− parts,
786− i,
787− len,
788− source;
932+ return data;
933+ },
789934
790− for (i in scripts) {
791− if (hasKey(scripts, i) && !scripts[i].src) {
792− inlineScriptBlocks.push(scripts[i]);
793− }
935+ _getHttpData: function() {
936+ if (!this._hasDocument || !document.location || !document.location.href) {
937+ return;
794938 }
795939
796− for (i = 2, len = lines.length; i < len; i += 2) {
797− var item = null;
798− if ((parts = lineRE1.exec(lines[i]))) {
799− item = {
800− 'url': parts[2],
801− 'func': parts[3],
802− 'line': +parts[1]
803− };
804− } else if ((parts = lineRE2.exec(lines[i]))) {
805− item = {
806− 'url': parts[3],
807− 'func': parts[4]
808− };
809− var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block
810− var script = inlineScriptBlocks[parts[2] - 1];
811− if (script) {
812− source = getSource(item.url);
813− if (source) {
814− source = source.join('\n');
815− var pos = source.indexOf(script.innerText);
816− if (pos >= 0) {
817− item.line = relativeLine + source.substring(0, pos).split('\n').length;
818− }
819− }
820− }
821− } else if ((parts = lineRE3.exec(lines[i]))) {
822− var url = window.location.href.replace(/#.*$/, ''),
823− line = parts[1];
824− var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[i + 1]));
825− source = findSourceInUrls(re, [url]);
826− item = {
827− 'url': url,
828− 'line': source ? source.line : line,
829− 'func': ''
830− };
940+ var httpData = {
941+ headers: {
942+ 'User-Agent': navigator.userAgent
831943 }
944+ };
832945
833− if (item) {
834− if (!item.func) {
835− item.func = guessFunctionName(item.url, item.line);
836− }
837− var context = gatherContext(item.url, item.line);
838− var midline = (context ? context[Math.floor(context.length / 2)] : null);
839− if (context && midline.replace(/^\s*/, '') === lines[i + 1].replace(/^\s*/, '')) {
840− item.context = context;
841− } else {
842− // 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);
843− item.context = [lines[i + 1]];
844− }
845− stack.push(item);
846− }
847− }
848− if (!stack.length) {
849− return null; // could not parse multiline exception message as Opera stack trace
946+ httpData.url = document.location.href;
947+
948+ if (document.referrer) {
949+ httpData.headers.Referer = document.referrer;
850950 }
851951
852− return {
853− 'name': ex.name,
854− 'message': lines[0],
855− 'url': getLocationHref(),
856− 'stack': stack
857− };
858− }
952+ return httpData;
953+ },
859954
860− /**
861− * Adds information about the first frame to incomplete stack traces.
862− * Safari and IE require this to get complete data on the first frame.
863− * @param {Object.<string, *>} stackInfo Stack trace information from
864− * one of the compute* methods.
865− * @param {string} url The URL of the script that caused an error.
866− * @param {(number|string)} lineNo The line number of the script that
867− * caused an error.
868− * @param {string=} message The error generated by the browser, which
869− * hopefully contains the name of the object that caused the error.
870− * @return {boolean} Whether or not the stack information was
871− * augmented.
872− */
873− function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
874− var initial = {
875− 'url': url,
876− 'line': lineNo
877− };
878955
879− if (initial.url && initial.line) {
880− stackInfo.incomplete = false;
956+ _send: function(data) {
957+ var self = this;
881958
882− if (!initial.func) {
883− initial.func = guessFunctionName(initial.url, initial.line);
884− }
959+ var globalOptions = this._globalOptions;
885960
886− if (!initial.context) {
887− initial.context = gatherContext(initial.url, initial.line);
888− }
961+ var baseData = {
962+ project: this._globalProject,
963+ logger: globalOptions.logger,
964+ platform: 'javascript'
965+ }, httpData = this._getHttpData();
889966
890− var reference = / '([^']+)' /.exec(message);
891− if (reference) {
892− initial.column = findSourceInLine(reference[1], initial.url, initial.line);
893− }
967+ if (httpData) {
968+ baseData.request = httpData;
969+ }
894970
895− if (stackInfo.stack.length > 0) {
896− if (stackInfo.stack[0].url === initial.url) {
897− if (stackInfo.stack[0].line === initial.line) {
898− return false; // already in stack trace
899− } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {
900− stackInfo.stack[0].line = initial.line;
901− stackInfo.stack[0].context = initial.context;
902− return false;
903− }
904− }
905− }
971+ data = objectMerge(baseData, data);
906972
907− stackInfo.stack.unshift(initial);
908− stackInfo.partial = true;
909− return true;
910− } else {
911− stackInfo.incomplete = true;
912− }
973+ // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
974+ data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags);
975+ data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra);
913976
914− return false;
915− }
977+ // Send along our own collected metadata with extra
978+ data.extra['session:duration'] = now() - this._startTime;
916979
917− /**
918− * Computes stack trace information by walking the arguments.caller
919− * chain at the time the exception occurred. This will cause earlier
920− * frames to be missed but is the only way to get any stack trace in
921− * Safari and IE. The top frame is restored by
922− * {@link augmentStackTraceWithInitialElement}.
923− * @param {Error} ex
924− * @return {?Object.<string, *>} Stack trace information.
925− */
926− function computeStackTraceByWalkingCallerChain(ex, depth) {
927− var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,
928− stack = [],
929− funcs = {},
930− recursion = false,
931− parts,
932− item,
933− source;
980+ // If there are no tags/extra, strip the key from the payload alltogther.
981+ if (isEmptyObject(data.tags)) delete data.tags;
934982
935− for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {
936− if (curr === computeStackTrace || curr === TraceKit.report) {
937− // console.log('skipping internal function');
938− continue;
939− }
983+ if (this._globalContext.user) {
984+ // sentry.interfaces.User
985+ data.user = this._globalContext.user;
986+ }
940987
941− item = {
942− 'url': null,
943− 'func': UNKNOWN_FUNCTION,
944− 'line': null,
945− 'column': null
946− };
988+ // Include the release if it's defined in globalOptions
989+ if (globalOptions.release) data.release = globalOptions.release;
947990
948− if (curr.name) {
949− item.func = curr.name;
950− } else if ((parts = functionName.exec(curr.toString()))) {
951− item.func = parts[1];
952− }
991+ // Include server_name if it's defined in globalOptions
992+ if (globalOptions.serverName) data.server_name = globalOptions.serverName;
953993
954− if (typeof item.func === 'undefined') {
955− try {
956− item.func = parts.input.substring(0, parts.input.indexOf('{'));
957− } catch (e) { }
958− }
994+ if (isFunction(globalOptions.dataCallback)) {
995+ data = globalOptions.dataCallback(data) || data;
996+ }
959997
960− if ((source = findSourceByFunctionBody(curr))) {
961− item.url = source.url;
962− item.line = source.line;
998+ // Why??????????
999+ if (!data || isEmptyObject(data)) {
1000+ return;
1001+ }
9631002
964− if (item.func === UNKNOWN_FUNCTION) {
965− item.func = guessFunctionName(item.url, item.line);
966− }
1003+ // Check if the request should be filtered or not
1004+ if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
1005+ return;
1006+ }
9671007
968− var reference = / '([^']+)' /.exec(ex.message || ex.description);
969− if (reference) {
970− item.column = findSourceInLine(reference[1], source.url, source.line);
971− }
972− }
1008+ // Send along an event_id if not explicitly passed.
1009+ // This event_id can be used to reference the error within Sentry itself.
1010+ // Set lastEventId after we know the error should actually be sent
1011+ this._lastEventId = data.event_id || (data.event_id = uuid4());
9731012
974− if (funcs['' + curr]) {
975− recursion = true;
976− }else{
977− funcs['' + curr] = true;
978− }
1013+ // Try and clean up the packet before sending by truncating long values
1014+ data = this._trimPacket(data);
9791015
980− stack.push(item);
981− }
1016+ this._logDebug('debug', 'Raven about to send:', data);
9821017
983− if (depth) {
984− // console.log('depth is ' + depth);
985− // console.log('stack is ' + stack.length);
986− stack.splice(0, depth);
987− }
1018+ if (!this.isSetup()) return;
9881019
989− var result = {
990− 'name': ex.name,
991− 'message': ex.message,
992− 'url': getLocationHref(),
993− 'stack': stack
1020+ var auth = {
1021+ sentry_version: '7',
1022+ sentry_client: 'raven-js/' + this.VERSION,
1023+ sentry_key: this._globalKey
9941024 };
995− augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);
996− return result;
997− }
998−
999− /**
1000− * Computes a stack trace for an exception.
1001− * @param {Error} ex
1002− * @param {(string|number)=} depth
1003− */
1004− function computeStackTrace(ex, depth) {
1005− var stack = null;
1006− depth = (depth == null ? 0 : +depth);
1025+ if (this._globalSecret) {
1026+ auth.sentry_secret = this._globalSecret;
1027+ }
10071028
1008− try {
1009− // This must be tried first because Opera 10 *destroys*
1010− // its stacktrace property if you try to access the stack
1011− // property first!!
1012− stack = computeStackTraceFromStacktraceProp(ex);
1013− if (stack) {
1014− return stack;
1015− }
1016− } catch (e) {
1017− if (TraceKit.debug) {
1018− throw e;
1029+ var url = this._globalEndpoint;
1030+ (globalOptions.transport || this._makeRequest).call(this, {
1031+ url: url,
1032+ auth: auth,
1033+ data: data,
1034+ options: globalOptions,
1035+ onSuccess: function success() {
1036+ self._triggerEvent('success', {
1037+ data: data,
1038+ src: url
1039+ });
1040+ },
1041+ onError: function failure() {
1042+ self._triggerEvent('failure', {
1043+ data: data,
1044+ src: url
1045+ });
10191046 }
1047+ });
1048+ },
1049+
1050+ _makeImageRequest: function(opts) {
1051+ // Tack on sentry_data to auth options, which get urlencoded
1052+ opts.auth.sentry_data = JSON.stringify(opts.data);
1053+
1054+ var img = this._newImage(),
1055+ src = opts.url + '?' + urlencode(opts.auth),
1056+ crossOrigin = opts.options.crossOrigin;
1057+
1058+ if (crossOrigin || crossOrigin === '') {
1059+ img.crossOrigin = crossOrigin;
10201060 }
1061+ img.onload = opts.onSuccess;
1062+ img.onerror = img.onabort = opts.onError;
1063+ img.src = src;
1064+ },
10211065
1022− try {
1023− stack = computeStackTraceFromStackProp(ex);
1024− if (stack) {
1025− return stack;
1026− }
1027− } catch (e) {
1028− if (TraceKit.debug) {
1029− throw e;
1066+ _makeXhrRequest: function(opts) {
1067+ var request;
1068+
1069+ var url = opts.url;
1070+ function handler() {
1071+ if (request.status === 200) {
1072+ if (opts.onSuccess) {
1073+ opts.onSuccess();
1074+ }
1075+ } else if (opts.onError) {
1076+ opts.onError();
10301077 }
10311078 }
10321079
1033− try {
1034− stack = computeStackTraceFromOperaMultiLineMessage(ex);
1035− if (stack) {
1036− return stack;
1037− }
1038− } catch (e) {
1039− if (TraceKit.debug) {
1040− throw e;
1041− }
1080+ request = new XMLHttpRequest();
1081+ if ('withCredentials' in request) {
1082+ request.onreadystatechange = function () {
1083+ if (request.readyState !== 4) {
1084+ return;
1085+ }
1086+ handler();
1087+ };
1088+ } else {
1089+ request = new XDomainRequest();
1090+ // xdomainrequest cannot go http -> https (or vice versa),
1091+ // so always use protocol relative
1092+ url = url.replace(/^https?:/, '');
1093+
1094+ // onreadystatechange not supported by XDomainRequest
1095+ request.onload = handler;
10421096 }
10431097
1044− try {
1045− stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);
1046− if (stack) {
1047− return stack;
1048− }
1049− } catch (e) {
1050− if (TraceKit.debug) {
1051− throw e;
1052− }
1098+ // NOTE: auth is intentionally sent as part of query string (NOT as custom
1099+ // HTTP header) so as to avoid preflight CORS requests
1100+ request.open('POST', url + '?' + urlencode(opts.auth));
1101+ request.send(JSON.stringify(opts.data));
1102+ },
1103+
1104+ _makeRequest: function(opts) {
1105+ var hasCORS =
1106+ 'withCredentials' in new XMLHttpRequest() ||
1107+ typeof XDomainRequest !== 'undefined';
1108+
1109+ return (hasCORS ? this._makeXhrRequest : this._makeImageRequest)(opts);
1110+ },
1111+
1112+ // Note: this is shitty, but I can't figure out how to get
1113+ // sinon to stub document.createElement without breaking everything
1114+ // so this wrapper is just so I can stub it for tests.
1115+ _newImage: function() {
1116+ return document.createElement('img');
1117+ },
1118+
1119+ _logDebug: function(level) {
1120+ if (this._originalConsoleMethods[level] && this.debug) {
1121+ // In IE<10 console methods do not have their own 'apply' method
1122+ Function.prototype.apply.call(
1123+ this._originalConsoleMethods[level],
1124+ this._originalConsole,
1125+ [].slice.call(arguments, 1)
1126+ );
10531127 }
1128+ },
10541129
1055− return {
1056− 'name': ex.name,
1057− 'message': ex.message,
1058− 'url': getLocationHref()
1059− };
1130+ _mergeContext: function(key, context) {
1131+ if (isUndefined(context)) {
1132+ delete this._globalContext[key];
1133+ } else {
1134+ this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context);
1135+ }
10601136 }
1137+};
10611138
1062− computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;
1063− computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp;
1064− computeStackTrace.guessFunctionName = guessFunctionName;
1065− computeStackTrace.gatherContext = gatherContext;
1139+// Deprecations
1140+Raven.prototype.setUser = Raven.prototype.setUserContext;
1141+Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
10661142
1067− return computeStackTrace;
1068−}());
1143+module.exports = Raven;
1144+
1145+},{"1":1,"4":4,"5":5}],3:[function(_dereq_,module,exports){
1146+/**
1147+ * Enforces a single instance of the Raven client, and the
1148+ * main entry point for Raven. If you are a consumer of the
1149+ * Raven library, you SHOULD load this file (vs raven.js).
1150+ **/
10691151
10701152 'use strict';
10711153
1072−// First, check for JSON support
1073−// If there is no JSON, we no-op the core features of Raven
1074−// since JSON is required to encode the payload
1075−var _Raven = window.Raven,
1076− hasJSON = !!(typeof JSON === 'object' && JSON.stringify),
1077− // Raven can run in contexts where there's no document (react-native)
1078− hasDocument = typeof document !== 'undefined',
1079− lastCapturedException,
1080− lastEventId,
1081− globalServer,
1082− globalKey,
1083− globalProject,
1084− globalContext = {},
1085− globalOptions = {
1086− logger: 'javascript',
1087− ignoreErrors: [],
1088− ignoreUrls: [],
1089− whitelistUrls: [],
1090− includePaths: [],
1091− crossOrigin: 'anonymous',
1092− collectWindowErrors: true,
1093− maxMessageLength: 100
1094− },
1095− isRavenInstalled = false,
1096− objectPrototype = Object.prototype,
1097− // capture references to window.console *and* all its methods first
1098− // before the console plugin has a chance to monkey patch
1099− originalConsole = window.console || {},
1100− originalConsoleMethods = {},
1101− plugins = [],
1102− startTime = now();
1154+var RavenConstructor = _dereq_(2);
1155+
1156+var _Raven = window.Raven;
1157+
1158+var Raven = new RavenConstructor();
11031159
1104−for (var method in originalConsole) {
1105− originalConsoleMethods[method] = originalConsole[method];
1106−}
11071160 /*
1108− * The core Raven singleton
1161+ * Allow multiple versions of Raven to be installed.
1162+ * Strip Raven from the global context and returns the instance.
11091163 *
1110− * @this {Raven}
1164+ * @return {Raven}
11111165 */
1112−var Raven = {
1113− VERSION: '1.3.0',
1166+Raven.noConflict = function () {
1167+ window.Raven = _Raven;
1168+ return Raven;
1169+};
11141170
1115− debug: false,
1171+Raven.afterLoad();
11161172
1117− /*
1118− * Allow multiple versions of Raven to be installed.
1119− * Strip Raven from the global context and returns the instance.
1120− *
1121− * @return {Raven}
1122− */
1123− noConflict: function() {
1124− window.Raven = _Raven;
1125− return Raven;
1126− },
1173+module.exports = Raven;
11271174
1128− /*
1129− * Configure Raven with a DSN and extra options
1130− *
1131− * @param {string} dsn The public Sentry DSN
1132− * @param {object} options Optional set of of global options [optional]
1133− * @return {Raven}
1134− */
1135− config: function(dsn, options) {
1136− if (globalServer) {
1137− logDebug('error', 'Error: Raven has already been configured');
1138− return Raven;
1139− }
1140− if (!dsn) return Raven;
1175+},{"2":2}],4:[function(_dereq_,module,exports){
1176+'use strict';
11411177
1142− var uri = parseDSN(dsn),
1143− lastSlash = uri.path.lastIndexOf('/'),
1144− path = uri.path.substr(1, lastSlash);
1178+var objectPrototype = Object.prototype;
11451179
1146− // merge in options
1147− if (options) {
1148− each(options, function(key, value){
1149− // tags and extra are special and need to be put into context
1150− if (key == 'tags' || key == 'extra') {
1151− globalContext[key] = value;
1152− } else {
1153− globalOptions[key] = value;
1154− }
1155− });
1156− }
1180+function isUndefined(what) {
1181+ return what === void 0;
1182+}
11571183
1158− // "Script error." is hard coded into browsers for errors that it can't read.
1159− // this is the result of a script being pulled in from an external domain and CORS.
1160− globalOptions.ignoreErrors.push(/^Script error\.?$/);
1161− globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
1184+function isFunction(what) {
1185+ return typeof what === 'function';
1186+}
11621187
1163− // join regexp rules into one big rule
1164− globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
1165− globalOptions.ignoreUrls = globalOptions.ignoreUrls.length ? joinRegExp(globalOptions.ignoreUrls) : false;
1166− globalOptions.whitelistUrls = globalOptions.whitelistUrls.length ? joinRegExp(globalOptions.whitelistUrls) : false;
1167− globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
1188+function isString(what) {
1189+ return objectPrototype.toString.call(what) === '[object String]';
1190+}
11681191
1169− globalKey = uri.user;
1170− globalProject = uri.path.substr(lastSlash + 1);
1192+function isObject(what) {
1193+ return typeof what === 'object' && what !== null;
1194+}
11711195
1172− // assemble the endpoint from the uri pieces
1173− globalServer = '//' + uri.host +
1174− (uri.port ? ':' + uri.port : '') +
1175− '/' + path + 'api/' + globalProject + '/store/';
1196+function isEmptyObject(what) {
1197+ for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars
1198+ return true;
1199+}
11761200
1177− if (uri.protocol) {
1178− globalServer = uri.protocol + ':' + globalServer;
1179− }
1201+// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
1202+// with some tiny modifications
1203+function isError(what) {
1204+ var toString = objectPrototype.toString.call(what);
1205+ return isObject(what) &&
1206+ toString === '[object Error]' ||
1207+ toString === '[object Exception]' || // Firefox NS_ERROR_FAILURE Exceptions
1208+ what instanceof Error;
1209+}
11801210
1181− if (globalOptions.fetchContext) {
1182− TraceKit.remoteFetching = true;
1183− }
1211+function each(obj, callback) {
1212+ var i, j;
11841213
1185− if (globalOptions.linesOfContext) {
1186− TraceKit.linesOfContext = globalOptions.linesOfContext;
1214+ if (isUndefined(obj.length)) {
1215+ for (i in obj) {
1216+ if (hasKey(obj, i)) {
1217+ callback.call(null, i, obj[i]);
1218+ }
11871219 }
1188−
1189− TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
1190−
1191− // return for chaining
1192− return Raven;
1193− },
1194−
1195− /*
1196− * Installs a global window.onerror error handler
1197− * to capture and report uncaught exceptions.
1198− * At this point, install() is required to be called due
1199− * to the way TraceKit is set up.
1200− *
1201− * @return {Raven}
1202− */
1203− install: function() {
1204− if (isSetup() && !isRavenInstalled) {
1205− TraceKit.report.subscribe(handleStackInfo);
1206−
1207− // Install all of the plugins
1208− each(plugins, function(_, plugin) {
1209− plugin();
1210− });
1211−
1212− isRavenInstalled = true;
1220+ } else {
1221+ j = obj.length;
1222+ if (j) {
1223+ for (i = 0; i < j; i++) {
1224+ callback.call(null, i, obj[i]);
1225+ }
12131226 }
1227+ }
1228+}
12141229
1215− return Raven;
1216− },
1230+function objectMerge(obj1, obj2) {
1231+ if (!obj2) {
1232+ return obj1;
1233+ }
1234+ each(obj2, function(key, value){
1235+ obj1[key] = value;
1236+ });
1237+ return obj1;
1238+}
12171239
1218− /*
1219− * Wrap code within a context so Raven can capture errors
1220− * reliably across domains that is executed immediately.
1221− *
1222− * @param {object} options A specific set of options for this context [optional]
1223− * @param {function} func The callback to be immediately executed within the context
1224− * @param {array} args An array of arguments to be called with the callback [optional]
1225− */
1226− context: function(options, func, args) {
1227− if (isFunction(options)) {
1228− args = func || [];
1229− func = options;
1230− options = undefined;
1231− }
1240+function truncate(str, max) {
1241+ return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
1242+}
12321243
1233− return Raven.wrap(options, func).apply(this, args);
1234− },
1244+/**
1245+ * hasKey, a better form of hasOwnProperty
1246+ * Example: hasKey(MainHostObject, property) === true/false
1247+ *
1248+ * @param {Object} host object to check property
1249+ * @param {string} key to check
1250+ */
1251+function hasKey(object, key) {
1252+ return objectPrototype.hasOwnProperty.call(object, key);
1253+}
12351254
1236− /*
1237− * Wrap code within a context and returns back a new function to be executed
1238− *
1239− * @param {object} options A specific set of options for this context [optional]
1240− * @param {function} func The function to be wrapped in a new context
1241− * @return {function} The newly wrapped functions with a context
1242− */
1243− wrap: function(options, func) {
1244− // 1 argument has been passed, and it's not a function
1245− // so just return it
1246− if (isUndefined(func) && !isFunction(options)) {
1247− return options;
1248− }
1255+function joinRegExp(patterns) {
1256+ // Combine an array of regular expressions and strings into one large regexp
1257+ // Be mad.
1258+ var sources = [],
1259+ i = 0, len = patterns.length,
1260+ pattern;
12491261
1250− // options is optional
1251− if (isFunction(options)) {
1252− func = options;
1253− options = undefined;
1262+ for (; i < len; i++) {
1263+ pattern = patterns[i];
1264+ if (isString(pattern)) {
1265+ // If it's a string, we need to escape it
1266+ // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
1267+ sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));
1268+ } else if (pattern && pattern.source) {
1269+ // If it's a regexp already, we want to extract the source
1270+ sources.push(pattern.source);
12541271 }
1272+ // Intentionally skip other cases
1273+ }
1274+ return new RegExp(sources.join('|'), 'i');
1275+}
12551276
1256− // At this point, we've passed along 2 arguments, and the second one
1257− // is not a function either, so we'll just return the second argument.
1258− if (!isFunction(func)) {
1259− return func;
1260− }
1277+function urlencode(o) {
1278+ var pairs = [];
1279+ each(o, function(key, value) {
1280+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
1281+ });
1282+ return pairs.join('&');
1283+}
12611284
1262− // We don't wanna wrap it twice!
1263− if (func.__raven__) {
1264− return func;
1265− }
1285+function uuid4() {
1286+ var crypto = window.crypto || window.msCrypto;
12661287
1267− function wrapped() {
1268− var args = [], i = arguments.length,
1269− deep = !options || options && options.deep !== false;
1270− // Recursively wrap all of a function's arguments that are
1271− // functions themselves.
1288+ if (!isUndefined(crypto) && crypto.getRandomValues) {
1289+ // Use window.crypto API if available
1290+ var arr = new Uint16Array(8);
1291+ crypto.getRandomValues(arr);
12721292
1273− while(i--) args[i] = deep ? Raven.wrap(options, arguments[i]) : arguments[i];
1293+ // set 4 in byte 7
1294+ arr[3] = arr[3] & 0xFFF | 0x4000;
1295+ // set 2 most significant bits of byte 9 to '10'
1296+ arr[4] = arr[4] & 0x3FFF | 0x8000;
12741297
1275− try {
1276− /*jshint -W040*/
1277− return func.apply(this, args);
1278− } catch(e) {
1279− Raven.captureException(e, options);
1280− throw e;
1298+ var pad = function(num) {
1299+ var v = num.toString(16);
1300+ while (v.length < 4) {
1301+ v = '0' + v;
12811302 }
1282− }
1303+ return v;
1304+ };
12831305
1284− // copy over properties of the old function
1285− for (var property in func) {
1286− if (hasKey(func, property)) {
1287− wrapped[property] = func[property];
1288− }
1289− }
1290− wrapped.prototype = func.prototype;
1306+ return pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
1307+ pad(arr[5]) + pad(arr[6]) + pad(arr[7]);
1308+ } else {
1309+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
1310+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1311+ var r = Math.random()*16|0,
1312+ v = c === 'x' ? r : r&0x3|0x8;
1313+ return v.toString(16);
1314+ });
1315+ }
1316+}
12911317
1292− // Signal that this function has been wrapped already
1293− // for both debugging and to prevent it to being wrapped twice
1294− wrapped.__raven__ = true;
1295− wrapped.__inner__ = func;
1318+module.exports = {
1319+ isUndefined: isUndefined,
1320+ isFunction: isFunction,
1321+ isString: isString,
1322+ isObject: isObject,
1323+ isEmptyObject: isEmptyObject,
1324+ isError: isError,
1325+ each: each,
1326+ objectMerge: objectMerge,
1327+ truncate: truncate,
1328+ hasKey: hasKey,
1329+ joinRegExp: joinRegExp,
1330+ urlencode: urlencode,
1331+ uuid4: uuid4
1332+};
12961333
1297− return wrapped;
1298− },
1334+},{}],5:[function(_dereq_,module,exports){
1335+'use strict';
12991336
1300− /*
1301− * Uninstalls the global error handler.
1302− *
1303− * @return {Raven}
1304− */
1305− uninstall: function() {
1306− TraceKit.report.uninstall();
1307− isRavenInstalled = false;
1337+var utils = _dereq_(4);
13081338
1309− return Raven;
1310− },
1339+var hasKey = utils.hasKey;
1340+var isString = utils.isString;
1341+var isUndefined = utils.isUndefined;
13111342
1312− /*
1313− * Manually capture an exception and send it over to Sentry
1314− *
1315− * @param {error} ex An exception to be logged
1316− * @param {object} options A specific set of options for this error [optional]
1317− * @return {Raven}
1343+/*
1344+ TraceKit - Cross brower stack traces - github.com/occ/TraceKit
1345+ MIT license
1346+*/
1347+
1348+var TraceKit = {
1349+ remoteFetching: false,
1350+ collectWindowErrors: true,
1351+ // 3 lines before, the offending line, 3 lines after
1352+ linesOfContext: 7,
1353+ debug: false
1354+};
1355+
1356+// global reference to slice
1357+var _slice = [].slice;
1358+var UNKNOWN_FUNCTION = '?';
1359+
1360+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
1361+var ERROR_TYPES_RE = /^(?:Uncaught )?((?:Eval|Internal|Range|Reference|Syntax|Type|URI)Error)\: ?(.*)$/;
1362+
1363+function getLocationHref() {
1364+ if (typeof document === 'undefined')
1365+ return '';
1366+
1367+ return document.location.href;
1368+}
1369+
1370+/**
1371+ * TraceKit.report: cross-browser processing of unhandled exceptions
1372+ *
1373+ * Syntax:
1374+ * TraceKit.report.subscribe(function(stackInfo) { ... })
1375+ * TraceKit.report.unsubscribe(function(stackInfo) { ... })
1376+ * TraceKit.report(exception)
1377+ * try { ...code... } catch(ex) { TraceKit.report(ex); }
1378+ *
1379+ * Supports:
1380+ * - Firefox: full stack trace with line numbers, plus column number
1381+ * on top frame; column number is not guaranteed
1382+ * - Opera: full stack trace with line and column numbers
1383+ * - Chrome: full stack trace with line and column numbers
1384+ * - Safari: line and column number for the top frame only; some frames
1385+ * may be missing, and column number is not guaranteed
1386+ * - IE: line and column number for the top frame only; some frames
1387+ * may be missing, and column number is not guaranteed
1388+ *
1389+ * In theory, TraceKit should work on all of the following versions:
1390+ * - IE5.5+ (only 8.0 tested)
1391+ * - Firefox 0.9+ (only 3.5+ tested)
1392+ * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
1393+ * Exceptions Have Stacktrace to be enabled in opera:config)
1394+ * - Safari 3+ (only 4+ tested)
1395+ * - Chrome 1+ (only 5+ tested)
1396+ * - Konqueror 3.5+ (untested)
1397+ *
1398+ * Requires TraceKit.computeStackTrace.
1399+ *
1400+ * Tries to catch all unhandled exceptions and report them to the
1401+ * subscribed handlers. Please note that TraceKit.report will rethrow the
1402+ * exception. This is REQUIRED in order to get a useful stack trace in IE.
1403+ * If the exception does not reach the top of the browser, you will only
1404+ * get a stack trace from the point where TraceKit.report was called.
1405+ *
1406+ * Handlers receive a stackInfo object as described in the
1407+ * TraceKit.computeStackTrace docs.
1408+ */
1409+TraceKit.report = (function reportModuleWrapper() {
1410+ var handlers = [],
1411+ lastArgs = null,
1412+ lastException = null,
1413+ lastExceptionStack = null;
1414+
1415+ /**
1416+ * Add a crash handler.
1417+ * @param {Function} handler
13181418 */
1319− captureException: function(ex, options) {
1320− // If not an Error is passed through, recall as a message instead
1321− if (!isError(ex)) return Raven.captureMessage(ex, options);
1419+ function subscribe(handler) {
1420+ installGlobalHandler();
1421+ handlers.push(handler);
1422+ }
13221423
1323− // Store the raw exception object for potential debugging and introspection
1324− lastCapturedException = ex;
1424+ /**
1425+ * Remove a crash handler.
1426+ * @param {Function} handler
1427+ */
1428+ function unsubscribe(handler) {
1429+ for (var i = handlers.length - 1; i >= 0; --i) {
1430+ if (handlers[i] === handler) {
1431+ handlers.splice(i, 1);
1432+ }
1433+ }
1434+ }
13251435
1326− // TraceKit.report will re-raise any exception passed to it,
1327− // which means you have to wrap it in try/catch. Instead, we
1328− // can wrap it here and only re-raise if TraceKit.report
1329− // raises an exception different from the one we asked to
1330− // report on.
1331− try {
1332− var stack = TraceKit.computeStackTrace(ex);
1333− handleStackInfo(stack, options);
1334− } catch(ex1) {
1335− if(ex !== ex1) {
1336− throw ex1;
1436+ /**
1437+ * Remove all crash handlers.
1438+ */
1439+ function unsubscribeAll() {
1440+ uninstallGlobalHandler();
1441+ handlers = [];
1442+ }
1443+
1444+ /**
1445+ * Dispatch stack information to all handlers.
1446+ * @param {Object.<string, *>} stack
1447+ */
1448+ function notifyHandlers(stack, isWindowError) {
1449+ var exception = null;
1450+ if (isWindowError && !TraceKit.collectWindowErrors) {
1451+ return;
1452+ }
1453+ for (var i in handlers) {
1454+ if (hasKey(handlers, i)) {
1455+ try {
1456+ handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
1457+ } catch (inner) {
1458+ exception = inner;
1459+ }
13371460 }
13381461 }
13391462
1340− return Raven;
1341− },
1463+ if (exception) {
1464+ throw exception;
1465+ }
1466+ }
13421467
1343− /*
1344− * Manually send a message to Sentry
1345− *
1346− * @param {string} msg A plain message to be captured in Sentry
1347− * @param {object} options A specific set of options for this message [optional]
1348− * @return {Raven}
1468+ var _oldOnerrorHandler, _onErrorHandlerInstalled;
1469+
1470+ /**
1471+ * Ensures all global unhandled exceptions are recorded.
1472+ * Supported by Gecko and IE.
1473+ * @param {string} message Error message.
1474+ * @param {string} url URL of script that generated the exception.
1475+ * @param {(number|string)} lineNo The line number at which the error
1476+ * occurred.
1477+ * @param {?(number|string)} colNo The column number at which the error
1478+ * occurred.
1479+ * @param {?Error} ex The actual Error object.
13491480 */
1350− captureMessage: function(msg, options) {
1351− // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
1352− // early call; we'll error on the side of logging anything called before configuration since it's
1353− // probably something you should see:
1354− if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(msg)) {
1355− return;
1481+ function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
1482+ var stack = null;
1483+
1484+ if (lastExceptionStack) {
1485+ TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
1486+ processLastException();
1487+ } else if (ex) {
1488+ // New chrome and blink send along a real error object
1489+ // Let's just report that like a normal error.
1490+ // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
1491+ stack = TraceKit.computeStackTrace(ex);
1492+ notifyHandlers(stack, true);
1493+ } else {
1494+ var location = {
1495+ 'url': url,
1496+ 'line': lineNo,
1497+ 'column': colNo
1498+ };
1499+ location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
1500+ location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
1501+
1502+ var name = undefined;
1503+ var msg = message; // must be new var or will modify original `arguments`
1504+ var groups;
1505+ if (isString(message)) {
1506+ var groups = message.match(ERROR_TYPES_RE);
1507+ if (groups) {
1508+ name = groups[1];
1509+ msg = groups[2];
1510+ }
1511+ }
1512+
1513+ stack = {
1514+ 'name': name,
1515+ 'message': msg,
1516+ 'url': getLocationHref(),
1517+ 'stack': [location]
1518+ };
1519+ notifyHandlers(stack, true);
13561520 }
13571521
1358− // Fire away!
1359− send(
1360− objectMerge({
1361− message: msg + '' // Make sure it's actually a string
1362− }, options)
1363− );
1522+ if (_oldOnerrorHandler) {
1523+ return _oldOnerrorHandler.apply(this, arguments);
1524+ }
13641525
1365− return Raven;
1366− },
1526+ return false;
1527+ }
13671528
1368− addPlugin: function(plugin) {
1369− plugins.push(plugin);
1370− if (isRavenInstalled) plugin();
1371− return Raven;
1372− },
1529+ function installGlobalHandler ()
1530+ {
1531+ if (_onErrorHandlerInstalled) {
1532+ return;
1533+ }
1534+ _oldOnerrorHandler = window.onerror;
1535+ window.onerror = traceKitWindowOnError;
1536+ _onErrorHandlerInstalled = true;
1537+ }
13731538
1374− /*
1375− * Set/clear a user to be sent along with the payload.
1376− *
1377− * @param {object} user An object representing user data [optional]
1378− * @return {Raven}
1379− */
1380− setUserContext: function(user) {
1381− // Intentionally do not merge here since that's an unexpected behavior.
1382− globalContext.user = user;
1539+ function uninstallGlobalHandler ()
1540+ {
1541+ if (!_onErrorHandlerInstalled) {
1542+ return;
1543+ }
1544+ window.onerror = _oldOnerrorHandler;
1545+ _onErrorHandlerInstalled = false;
1546+ _oldOnerrorHandler = undefined;
1547+ }
13831548
1384− return Raven;
1385− },
1549+ function processLastException() {
1550+ var _lastExceptionStack = lastExceptionStack,
1551+ _lastArgs = lastArgs;
1552+ lastArgs = null;
1553+ lastExceptionStack = null;
1554+ lastException = null;
1555+ notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));
1556+ }
13861557
1387− /*
1388− * Merge extra attributes to be sent along with the payload.
1389− *
1390− * @param {object} extra An object representing extra data [optional]
1391− * @return {Raven}
1558+ /**
1559+ * Reports an unhandled Error to TraceKit.
1560+ * @param {Error} ex
1561+ * @param {?boolean} rethrow If false, do not re-throw the exception.
1562+ * Only used for window.onerror to not cause an infinite loop of
1563+ * rethrowing.
13921564 */
1393− setExtraContext: function(extra) {
1394− mergeContext('extra', extra);
1565+ function report(ex, rethrow) {
1566+ var args = _slice.call(arguments, 1);
1567+ if (lastExceptionStack) {
1568+ if (lastException === ex) {
1569+ return; // already caught by an inner catch block, ignore
1570+ } else {
1571+ processLastException();
1572+ }
1573+ }
13951574
1396− return Raven;
1397− },
1575+ var stack = TraceKit.computeStackTrace(ex);
1576+ lastExceptionStack = stack;
1577+ lastException = ex;
1578+ lastArgs = args;
13981579
1399− /*
1400− * Merge tags to be sent along with the payload.
1401− *
1402− * @param {object} tags An object representing tags [optional]
1403− * @return {Raven}
1580+ // If the stack trace is incomplete, wait for 2 seconds for
1581+ // slow slow IE to see if onerror occurs or not before reporting
1582+ // this exception; otherwise, we will end up with an incomplete
1583+ // stack trace
1584+ window.setTimeout(function () {
1585+ if (lastException === ex) {
1586+ processLastException();
1587+ }
1588+ }, (stack.incomplete ? 2000 : 0));
1589+
1590+ if (rethrow !== false) {
1591+ throw ex; // re-throw to propagate to the top level (and cause window.onerror)
1592+ }
1593+ }
1594+
1595+ report.subscribe = subscribe;
1596+ report.unsubscribe = unsubscribe;
1597+ report.uninstall = unsubscribeAll;
1598+ return report;
1599+}());
1600+
1601+/**
1602+ * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
1603+ *
1604+ * Syntax:
1605+ * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
1606+ * Returns:
1607+ * s.name - exception name
1608+ * s.message - exception message
1609+ * s.stack[i].url - JavaScript or HTML file URL
1610+ * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)
1611+ * s.stack[i].args - arguments passed to the function, if known
1612+ * s.stack[i].line - line number, if known
1613+ * s.stack[i].column - column number, if known
1614+ * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#
1615+ *
1616+ * Supports:
1617+ * - Firefox: full stack trace with line numbers and unreliable column
1618+ * number on top frame
1619+ * - Opera 10: full stack trace with line and column numbers
1620+ * - Opera 9-: full stack trace with line numbers
1621+ * - Chrome: full stack trace with line and column numbers
1622+ * - Safari: line and column number for the topmost stacktrace element
1623+ * only
1624+ * - IE: no line numbers whatsoever
1625+ *
1626+ * Tries to guess names of anonymous functions by looking for assignments
1627+ * in the source code. In IE and Safari, we have to guess source file names
1628+ * by searching for function bodies inside all page scripts. This will not
1629+ * work for scripts that are loaded cross-domain.
1630+ * Here be dragons: some function names may be guessed incorrectly, and
1631+ * duplicate functions may be mismatched.
1632+ *
1633+ * TraceKit.computeStackTrace should only be used for tracing purposes.
1634+ * Logging of unhandled exceptions should be done with TraceKit.report,
1635+ * which builds on top of TraceKit.computeStackTrace and provides better
1636+ * IE support by utilizing the window.onerror event to retrieve information
1637+ * about the top of the stack.
1638+ *
1639+ * Note: In IE and Safari, no stack trace is recorded on the Error object,
1640+ * so computeStackTrace instead walks its *own* chain of callers.
1641+ * This means that:
1642+ * * in Safari, some methods may be missing from the stack trace;
1643+ * * in IE, the topmost function in the stack trace will always be the
1644+ * caller of computeStackTrace.
1645+ *
1646+ * This is okay for tracing (because you are likely to be calling
1647+ * computeStackTrace from the function you want to be the topmost element
1648+ * of the stack trace anyway), but not okay for logging unhandled
1649+ * exceptions (because your catch block will likely be far away from the
1650+ * inner function that actually caused the exception).
1651+ *
1652+ */
1653+TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
1654+ var sourceCache = {};
1655+
1656+ /**
1657+ * Attempts to retrieve source code via XMLHttpRequest, which is used
1658+ * to look up anonymous function names.
1659+ * @param {string} url URL of source code.
1660+ * @return {string} Source contents.
14041661 */
1405− setTagsContext: function(tags) {
1406− mergeContext('tags', tags);
1662+ function loadSource(url) {
1663+ if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.
1664+ return '';
1665+ }
1666+ try {
1667+ var getXHR = function() {
1668+ try {
1669+ return new window.XMLHttpRequest();
1670+ } catch (e) {
1671+ // explicitly bubble up the exception if not found
1672+ return new window.ActiveXObject('Microsoft.XMLHTTP');
1673+ }
1674+ };
14071675
1408− return Raven;
1409− },
1676+ var request = getXHR();
1677+ request.open('GET', url, false);
1678+ request.send('');
1679+ return request.responseText;
1680+ } catch (e) {
1681+ return '';
1682+ }
1683+ }
14101684
1411− /*
1412− * Clear all of the context.
1413− *
1414− * @return {Raven}
1685+ /**
1686+ * Retrieves source code from the source code cache.
1687+ * @param {string} url URL of source code.
1688+ * @return {Array.<string>} Source contents.
14151689 */
1416− clearContext: function() {
1417− globalContext = {};
1690+ function getSource(url) {
1691+ if (!isString(url)) return [];
1692+ if (!hasKey(sourceCache, url)) {
1693+ // URL needs to be able to fetched within the acceptable domain. Otherwise,
1694+ // cross-domain errors will be triggered.
1695+ var source = '';
1696+ var domain = '';
1697+ try { domain = document.domain; } catch (e) {}
1698+ if (url.indexOf(domain) !== -1) {
1699+ source = loadSource(url);
1700+ }
1701+ sourceCache[url] = source ? source.split('\n') : [];
1702+ }
14181703
1419− return Raven;
1420− },
1704+ return sourceCache[url];
1705+ }
14211706
1422− /*
1423− * Get a copy of the current context. This cannot be mutated.
1424− *
1425− * @return {object} copy of context
1707+ /**
1708+ * Tries to use an externally loaded copy of source code to determine
1709+ * the name of a function by looking at the name of the variable it was
1710+ * assigned to, if any.
1711+ * @param {string} url URL of source code.
1712+ * @param {(string|number)} lineNo Line number in source code.
1713+ * @return {string} The function name, if discoverable.
14261714 */
1427− getContext: function() {
1428− // lol javascript
1429− return JSON.parse(JSON.stringify(globalContext));
1430− },
1715+ function guessFunctionName(url, lineNo) {
1716+ var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/,
1717+ reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,
1718+ line = '',
1719+ maxLines = 10,
1720+ source = getSource(url),
1721+ m;
14311722
1432− /*
1433− * Set release version of application
1434− *
1435− * @param {string} release Typically something like a git SHA to identify version
1436− * @return {Raven}
1723+ if (!source.length) {
1724+ return UNKNOWN_FUNCTION;
1725+ }
1726+
1727+ // Walk backwards from the first line in the function until we find the line which
1728+ // matches the pattern above, which is the function definition
1729+ for (var i = 0; i < maxLines; ++i) {
1730+ line = source[lineNo - i] + line;
1731+
1732+ if (!isUndefined(line)) {
1733+ if ((m = reGuessFunction.exec(line))) {
1734+ return m[1];
1735+ } else if ((m = reFunctionArgNames.exec(line))) {
1736+ return m[1];
1737+ }
1738+ }
1739+ }
1740+
1741+ return UNKNOWN_FUNCTION;
1742+ }
1743+
1744+ /**
1745+ * Retrieves the surrounding lines from where an exception occurred.
1746+ * @param {string} url URL of source code.
1747+ * @param {(string|number)} line Line number in source code to centre
1748+ * around for context.
1749+ * @return {?Array.<string>} Lines of source code.
14371750 */
1438− setRelease: function(release) {
1439− globalOptions.release = release;
1751+ function gatherContext(url, line) {
1752+ var source = getSource(url);
14401753
1441− return Raven;
1442− },
1754+ if (!source.length) {
1755+ return null;
1756+ }
14431757
1444− /*
1445− * Set the dataCallback option
1446− *
1447− * @param {function} callback The callback to run which allows the
1448− * data blob to be mutated before sending
1449− * @return {Raven}
1450− */
1451− setDataCallback: function(callback) {
1452− globalOptions.dataCallback = callback;
1758+ var context = [],
1759+ // linesBefore & linesAfter are inclusive with the offending line.
1760+ // if linesOfContext is even, there will be one extra line
1761+ // *before* the offending line.
1762+ linesBefore = Math.floor(TraceKit.linesOfContext / 2),
1763+ // Add one extra line if linesOfContext is odd
1764+ linesAfter = linesBefore + (TraceKit.linesOfContext % 2),
1765+ start = Math.max(0, line - linesBefore - 1),
1766+ end = Math.min(source.length, line + linesAfter - 1);
14531767
1454− return Raven;
1455− },
1768+ line -= 1; // convert to 0-based index
14561769
1457− /*
1458− * Set the shouldSendCallback option
1459− *
1460− * @param {function} callback The callback to run which allows
1461− * introspecting the blob before sending
1462− * @return {Raven}
1463− */
1464− setShouldSendCallback: function(callback) {
1465− globalOptions.shouldSendCallback = callback;
1770+ for (var i = start; i < end; ++i) {
1771+ if (!isUndefined(source[i])) {
1772+ context.push(source[i]);
1773+ }
1774+ }
14661775
1467− return Raven;
1468− },
1776+ return context.length > 0 ? context : null;
1777+ }
14691778
14701779 /**
1471− * Override the default HTTP transport mechanism that transmits data
1472− * to the Sentry server.
1473− *
1474− * @param {function} transport Function invoked instead of the default
1475− * `makeRequest` handler.
1476− *
1477− * @return {Raven}
1780+ * Escapes special characters, except for whitespace, in a string to be
1781+ * used inside a regular expression as a string literal.
1782+ * @param {string} text The string.
1783+ * @return {string} The escaped string literal.
14781784 */
1479− setTransport: function(transport) {
1480− globalOptions.transport = transport;
1481−
1482− return Raven;
1483− },
1785+ function escapeRegExp(text) {
1786+ return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
1787+ }
14841788
1485− /*
1486− * Get the latest raw exception that was captured by Raven.
1487− *
1488− * @return {error}
1789+ /**
1790+ * Escapes special characters in a string to be used inside a regular
1791+ * expression as a string literal. Also ensures that HTML entities will
1792+ * be matched the same as their literal friends.
1793+ * @param {string} body The string.
1794+ * @return {string} The escaped string.
14891795 */
1490− lastException: function() {
1491− return lastCapturedException;
1492− },
1796+ function escapeCodeAsRegExpForMatchingInsideHTML(body) {
1797+ return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('"', '(?:"|&quot;)').replace(/\s+/g, '\\s+');
1798+ }
14931799
1494− /*
1495− * Get the last event id
1496− *
1497− * @return {string}
1800+ /**
1801+ * Determines where a code fragment occurs in the source code.
1802+ * @param {RegExp} re The function definition.
1803+ * @param {Array.<string>} urls A list of URLs to search.
1804+ * @return {?Object.<string, (string|number)>} An object containing
1805+ * the url, line, and column number of the defined function.
14981806 */
1499− lastEventId: function() {
1500− return lastEventId;
1501− },
1807+ function findSourceInUrls(re, urls) {
1808+ var source, m;
1809+ for (var i = 0, j = urls.length; i < j; ++i) {
1810+ // console.log('searching', urls[i]);
1811+ if ((source = getSource(urls[i])).length) {
1812+ source = source.join('\n');
1813+ if ((m = re.exec(source))) {
1814+ // console.log('Found function in ' + urls[i]);
15021815
1503− /*
1504− * Determine if Raven is setup and ready to go.
1505− *
1506− * @return {boolean}
1507− */
1508− isSetup: function() {
1509− return isSetup();
1510− }
1511−};
1816+ return {
1817+ 'url': urls[i],
1818+ 'line': source.substring(0, m.index).split('\n').length,
1819+ 'column': m.index - source.lastIndexOf('\n', m.index) - 1
1820+ };
1821+ }
1822+ }
1823+ }
15121824
1513−// Deprecations
1514−Raven.setUser = Raven.setUserContext;
1515−Raven.setReleaseContext = Raven.setRelease;
1825+ // console.log('no match');
15161826
1517−function triggerEvent(eventType, options) {
1518− // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
1519− var evt, key;
1827+ return null;
1828+ }
15201829
1521− if (!hasDocument)
1522− return;
1830+ /**
1831+ * Determines at which column a code fragment occurs on a line of the
1832+ * source code.
1833+ * @param {string} fragment The code fragment.
1834+ * @param {string} url The URL to search.
1835+ * @param {(string|number)} line The line number to examine.
1836+ * @return {?number} The column number.
1837+ */
1838+ function findSourceInLine(fragment, url, line) {
1839+ var source = getSource(url),
1840+ re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'),
1841+ m;
15231842
1524− options = options || {};
1843+ line -= 1;
15251844
1526− eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
1845+ if (source && source.length > line && (m = re.exec(source[line]))) {
1846+ return m.index;
1847+ }
15271848
1528− if (document.createEvent) {
1529− evt = document.createEvent('HTMLEvents');
1530− evt.initEvent(eventType, true, true);
1531− } else {
1532− evt = document.createEventObject();
1533− evt.eventType = eventType;
1849+ return null;
15341850 }
15351851
1536− for (key in options) if (hasKey(options, key)) {
1537− evt[key] = options[key];
1538− }
1852+ /**
1853+ * Determines where a function was defined within the source code.
1854+ * @param {(Function|string)} func A function reference or serialized
1855+ * function definition.
1856+ * @return {?Object.<string, (string|number)>} An object containing
1857+ * the url, line, and column number of the defined function.
1858+ */
1859+ function findSourceByFunctionBody(func) {
1860+ if (typeof document === 'undefined')
1861+ return;
15391862
1540− if (document.createEvent) {
1541− // IE9 if standards
1542− document.dispatchEvent(evt);
1543− } else {
1544− // IE8 regardless of Quirks or Standards
1545− // IE9 if quirks
1546− try {
1547− document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
1548− } catch(e) {}
1549− }
1550−}
1863+ var urls = [window.location.href],
1864+ scripts = document.getElementsByTagName('script'),
1865+ body,
1866+ code = '' + func,
1867+ codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
1868+ eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
1869+ re,
1870+ parts,
1871+ result;
15511872
1552−var dsnKeys = 'source protocol user pass host port path'.split(' '),
1553− dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
1873+ for (var i = 0; i < scripts.length; ++i) {
1874+ var script = scripts[i];
1875+ if (script.src) {
1876+ urls.push(script.src);
1877+ }
1878+ }
15541879
1555−function RavenConfigError(message) {
1556− this.name = 'RavenConfigError';
1557− this.message = message;
1558−}
1559−RavenConfigError.prototype = new Error();
1560−RavenConfigError.prototype.constructor = RavenConfigError;
1880+ if (!(parts = codeRE.exec(code))) {
1881+ re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+'));
1882+ }
15611883
1562−/**** Private functions ****/
1563−function parseDSN(str) {
1564− var m = dsnPattern.exec(str),
1565− dsn = {},
1566− i = 7;
1884+ // not sure if this is really necessary, but I don’t have a test
1885+ // corpus large enough to confirm that and it was in the original.
1886+ else {
1887+ var name = parts[1] ? '\\s+' + parts[1] : '',
1888+ args = parts[2].split(',').join('\\s*,\\s*');
15671889
1568− try {
1569− while (i--) dsn[dsnKeys[i]] = m[i] || '';
1570− } catch(e) {
1571− throw new RavenConfigError('Invalid DSN: ' + str);
1572− }
1890+ body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+');
1891+ re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}');
1892+ }
15731893
1574− if (dsn.pass)
1575− throw new RavenConfigError('Do not specify your private key in the DSN!');
1894+ // look for a normal function definition
1895+ if ((result = findSourceInUrls(re, urls))) {
1896+ return result;
1897+ }
15761898
1577− return dsn;
1578−}
1899+ // look for an old-school event handler function
1900+ if ((parts = eventRE.exec(code))) {
1901+ var event = parts[1];
1902+ body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);
15791903
1580−function isUndefined(what) {
1581− return what === void 0;
1582−}
1904+ // look for a function defined in HTML as an onXXX handler
1905+ re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i');
15831906
1584−function isFunction(what) {
1585− return typeof what === 'function';
1586−}
1907+ if ((result = findSourceInUrls(re, urls[0]))) {
1908+ return result;
1909+ }
15871910
1588−function isString(what) {
1589− return objectPrototype.toString.call(what) === '[object String]';
1590−}
1911+ // look for ???
1912+ re = new RegExp(body);
15911913
1592−function isObject(what) {
1593− return typeof what === 'object' && what !== null;
1594−}
1914+ if ((result = findSourceInUrls(re, urls))) {
1915+ return result;
1916+ }
1917+ }
15951918
1596−function isEmptyObject(what) {
1597− for (var k in what) return false;
1598− return true;
1599−}
1919+ return null;
1920+ }
16001921
1601−// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
1602−// with some tiny modifications
1603−function isError(what) {
1604− return isObject(what) &&
1605− objectPrototype.toString.call(what) === '[object Error]' ||
1606− what instanceof Error;
1607−}
1922+ // Contents of Exception in various browsers.
1923+ //
1924+ // SAFARI:
1925+ // ex.message = Can't find variable: qq
1926+ // ex.line = 59
1927+ // ex.sourceId = 580238192
1928+ // ex.sourceURL = http://...
1929+ // ex.expressionBeginOffset = 96
1930+ // ex.expressionCaretOffset = 98
1931+ // ex.expressionEndOffset = 98
1932+ // ex.name = ReferenceError
1933+ //
1934+ // FIREFOX:
1935+ // ex.message = qq is not defined
1936+ // ex.fileName = http://...
1937+ // ex.lineNumber = 59
1938+ // ex.columnNumber = 69
1939+ // ex.stack = ...stack trace... (see the example below)
1940+ // ex.name = ReferenceError
1941+ //
1942+ // CHROME:
1943+ // ex.message = qq is not defined
1944+ // ex.name = ReferenceError
1945+ // ex.type = not_defined
1946+ // ex.arguments = ['aa']
1947+ // ex.stack = ...stack trace...
1948+ //
1949+ // INTERNET EXPLORER:
1950+ // ex.message = ...
1951+ // ex.name = ReferenceError
1952+ //
1953+ // OPERA:
1954+ // ex.message = ...message... (see the example below)
1955+ // ex.name = ReferenceError
1956+ // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
1957+ // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
16081958
1609−/**
1610− * hasKey, a better form of hasOwnProperty
1611− * Example: hasKey(MainHostObject, property) === true/false
1612− *
1613− * @param {Object} host object to check property
1614− * @param {string} key to check
1615− */
1616−function hasKey(object, key) {
1617− return objectPrototype.hasOwnProperty.call(object, key);
1618−}
1959+ /**
1960+ * Computes stack trace information from the stack property.
1961+ * Chrome and Gecko use this property.
1962+ * @param {Error} ex
1963+ * @return {?Object.<string, *>} Stack trace information.
1964+ */
1965+ function computeStackTraceFromStackProp(ex) {
1966+ if (isUndefined(ex.stack) || !ex.stack) return;
16191967
1620−function each(obj, callback) {
1621− var i, j;
1968+ var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|<anonymous>).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,
1969+ gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i,
1970+ winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|https?|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
1971+ lines = ex.stack.split('\n'),
1972+ stack = [],
1973+ parts,
1974+ element,
1975+ reference = /^(.*) is undefined$/.exec(ex.message);
16221976
1623− if (isUndefined(obj.length)) {
1624− for (i in obj) {
1625− if (hasKey(obj, i)) {
1626− callback.call(null, i, obj[i]);
1627− }
1628− }
1629− } else {
1630− j = obj.length;
1631− if (j) {
1632− for (i = 0; i < j; i++) {
1633− callback.call(null, i, obj[i]);
1977+ for (var i = 0, j = lines.length; i < j; ++i) {
1978+ if ((parts = chrome.exec(lines[i]))) {
1979+ var isNative = parts[2] && parts[2].indexOf('native') !== -1;
1980+ element = {
1981+ 'url': !isNative ? parts[2] : null,
1982+ 'func': parts[1] || UNKNOWN_FUNCTION,
1983+ 'args': isNative ? [parts[2]] : [],
1984+ 'line': parts[3] ? +parts[3] : null,
1985+ 'column': parts[4] ? +parts[4] : null
1986+ };
1987+ } else if ( parts = winjs.exec(lines[i]) ) {
1988+ element = {
1989+ 'url': parts[2],
1990+ 'func': parts[1] || UNKNOWN_FUNCTION,
1991+ 'args': [],
1992+ 'line': +parts[3],
1993+ 'column': parts[4] ? +parts[4] : null
1994+ };
1995+ } else if ((parts = gecko.exec(lines[i]))) {
1996+ element = {
1997+ 'url': parts[3],
1998+ 'func': parts[1] || UNKNOWN_FUNCTION,
1999+ 'args': parts[2] ? parts[2].split(',') : [],
2000+ 'line': parts[4] ? +parts[4] : null,
2001+ 'column': parts[5] ? +parts[5] : null
2002+ };
2003+ } else {
2004+ continue;
16342005 }
1635− }
1636− }
1637−}
16382006
1639−function handleStackInfo(stackInfo, options) {
1640− var frames = [];
1641−
1642− if (stackInfo.stack && stackInfo.stack.length) {
1643− each(stackInfo.stack, function(i, stack) {
1644− var frame = normalizeFrame(stack);
1645− if (frame) {
1646− frames.push(frame);
2007+ if (!element.func && element.line) {
2008+ element.func = guessFunctionName(element.url, element.line);
16472009 }
1648− });
1649− }
1650−
1651− triggerEvent('handle', {
1652− stackInfo: stackInfo,
1653− options: options
1654− });
1655−
1656− processException(
1657− stackInfo.name,
1658− stackInfo.message,
1659− stackInfo.url,
1660− stackInfo.lineno,
1661− frames,
1662− options
1663− );
1664−}
1665−
1666−function normalizeFrame(frame) {
1667− if (!frame.url) return;
1668−
1669− // normalize the frames data
1670− var normalized = {
1671− filename: frame.url,
1672− lineno: frame.line,
1673− colno: frame.column,
1674− 'function': frame.func || '?'
1675− }, context = extractContextFromFrame(frame), i;
1676−
1677− if (context) {
1678− var keys = ['pre_context', 'context_line', 'post_context'];
1679− i = 3;
1680− while (i--) normalized[keys[i]] = context[i];
1681− }
1682−
1683− normalized.in_app = !( // determine if an exception came from outside of our app
1684− // first we check the global includePaths list.
1685− (!!globalOptions.includePaths.test && !globalOptions.includePaths.test(normalized.filename)) ||
1686− // Now we check for fun, if the function name is Raven or TraceKit
1687− /(Raven|TraceKit)\./.test(normalized['function']) ||
1688− // finally, we do a last ditch effort and check for raven.min.js
1689− /raven\.(min\.)?js$/.test(normalized.filename)
1690− );
16912010
1692− return normalized;
1693−}
1694−
1695−function extractContextFromFrame(frame) {
1696− // immediately check if we should even attempt to parse a context
1697− if (!frame.context || !globalOptions.fetchContext) return;
1698−
1699− var context = frame.context,
1700− pivot = ~~(context.length / 2),
1701− i = context.length, isMinified = false;
2011+ if (element.line) {
2012+ element.context = gatherContext(element.url, element.line);
2013+ }
17022014
1703− while (i--) {
1704− // We're making a guess to see if the source is minified or not.
1705− // To do that, we make the assumption if *any* of the lines passed
1706− // in are greater than 300 characters long, we bail.
1707− // Sentry will see that there isn't a context
1708− if (context[i].length > 300) {
1709− isMinified = true;
1710− break;
2015+ stack.push(element);
17112016 }
1712− }
17132017
1714− if (isMinified) {
1715− // The source is minified and we don't know which column. Fuck it.
1716− if (isUndefined(frame.column)) return;
1717−
1718− // If the source is minified and has a frame column
1719− // we take a chunk of the offending line to hopefully shed some light
1720− return [
1721− [], // no pre_context
1722− context[pivot].substr(frame.column, 50), // grab 50 characters, starting at the offending column
1723− [] // no post_context
1724− ];
1725− }
2018+ if (!stack.length) {
2019+ return null;
2020+ }
17262021
1727− return [
1728− context.slice(0, pivot), // pre_context
1729− context[pivot], // context_line
1730− context.slice(pivot + 1) // post_context
1731− ];
1732−}
2022+ if (stack[0].line && !stack[0].column && reference) {
2023+ stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
2024+ } else if (!stack[0].column && !isUndefined(ex.columnNumber)) {
2025+ // FireFox uses this awesome columnNumber property for its top frame
2026+ // Also note, Firefox's column number is 0-based and everything else expects 1-based,
2027+ // so adding 1
2028+ stack[0].column = ex.columnNumber + 1;
2029+ }
17332030
1734−function processException(type, message, fileurl, lineno, frames, options) {
1735− var stacktrace, i, fullMessage;
1736−
1737− if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return;
1738−
1739− message += '';
1740− fullMessage = type + ': ' + message;
1741−
1742− if (frames && frames.length) {
1743− fileurl = frames[0].filename || fileurl;
1744− // Sentry expects frames oldest to newest
1745− // and JS sends them as newest to oldest
1746− frames.reverse();
1747− stacktrace = {frames: frames};
1748− } else if (fileurl) {
1749− stacktrace = {
1750− frames: [{
1751− filename: fileurl,
1752− lineno: lineno,
1753− in_app: true
1754− }]
2031+ return {
2032+ 'name': ex.name,
2033+ 'message': ex.message,
2034+ 'url': getLocationHref(),
2035+ 'stack': stack
17552036 };
17562037 }
17572038
1758− if (!!globalOptions.ignoreUrls.test && globalOptions.ignoreUrls.test(fileurl)) return;
1759− if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return;
2039+ /**
2040+ * Computes stack trace information from the stacktrace property.
2041+ * Opera 10 uses this property.
2042+ * @param {Error} ex
2043+ * @return {?Object.<string, *>} Stack trace information.
2044+ */
2045+ function computeStackTraceFromStacktraceProp(ex) {
2046+ // Access and store the stacktrace property before doing ANYTHING
2047+ // else to it because Opera is not very good at providing it
2048+ // reliably in other circumstances.
2049+ var stacktrace = ex.stacktrace;
2050+ if (isUndefined(ex.stacktrace) || !ex.stacktrace) return;
17602051
1761− // Fire away!
1762− send(
1763− objectMerge({
1764− // sentry.interfaces.Exception
1765− exception: {
1766− values: [{
1767− type: type,
1768− value: message,
1769− stacktrace: stacktrace
1770− }]
1771− },
1772− culprit: fileurl,
1773− message: fullMessage
1774− }, options)
1775− );
1776−}
2052+ var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i,
2053+ opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i,
2054+ lines = stacktrace.split('\n'),
2055+ stack = [],
2056+ parts;
17772057
1778−function objectMerge(obj1, obj2) {
1779− if (!obj2) {
1780− return obj1;
1781− }
1782− each(obj2, function(key, value){
1783− obj1[key] = value;
1784− });
1785− return obj1;
1786−}
2058+ for (var line = 0; line < lines.length; line += 2) {
2059+ var element = null;
2060+ if ((parts = opera10Regex.exec(lines[line]))) {
2061+ element = {
2062+ 'url': parts[2],
2063+ 'line': +parts[1],
2064+ 'column': null,
2065+ 'func': parts[3],
2066+ 'args':[]
2067+ };
2068+ } else if ((parts = opera11Regex.exec(lines[line]))) {
2069+ element = {
2070+ 'url': parts[6],
2071+ 'line': +parts[1],
2072+ 'column': +parts[2],
2073+ 'func': parts[3] || parts[4],
2074+ 'args': parts[5] ? parts[5].split(',') : []
2075+ };
2076+ }
17872077
1788−function truncate(str, max) {
1789− return str.length <= max ? str : str.substr(0, max) + '\u2026';
1790−}
2078+ if (element) {
2079+ if (!element.func && element.line) {
2080+ element.func = guessFunctionName(element.url, element.line);
2081+ }
2082+ if (element.line) {
2083+ try {
2084+ element.context = gatherContext(element.url, element.line);
2085+ } catch (exc) {}
2086+ }
17912087
1792−function trimPacket(data) {
1793− // For now, we only want to truncate the two different messages
1794− // but this could/should be expanded to just trim everything
1795− var max = globalOptions.maxMessageLength;
1796− data.message = truncate(data.message, max);
1797− if (data.exception) {
1798− var exception = data.exception.values[0];
1799− exception.value = truncate(exception.value, max);
1800− }
2088+ if (!element.context) {
2089+ element.context = [lines[line + 1]];
2090+ }
18012091
1802− return data;
1803−}
2092+ stack.push(element);
2093+ }
2094+ }
18042095
1805−function now() {
1806− return +new Date();
1807−}
2096+ if (!stack.length) {
2097+ return null;
2098+ }
18082099
1809−function getHttpData() {
1810− if (!hasDocument || !document.location || !document.location.href) {
1811− return;
2100+ return {
2101+ 'name': ex.name,
2102+ 'message': ex.message,
2103+ 'url': getLocationHref(),
2104+ 'stack': stack
2105+ };
18122106 }
18132107
1814− var httpData = {
1815− headers: {
1816− 'User-Agent': navigator.userAgent
2108+ /**
2109+ * NOT TESTED.
2110+ * Computes stack trace information from an error message that includes
2111+ * the stack trace.
2112+ * Opera 9 and earlier use this method if the option to show stack
2113+ * traces is turned on in opera:config.
2114+ * @param {Error} ex
2115+ * @return {?Object.<string, *>} Stack information.
2116+ */
2117+ function computeStackTraceFromOperaMultiLineMessage(ex) {
2118+ // Opera includes a stack trace into the exception message. An example is:
2119+ //
2120+ // Statement on line 3: Undefined variable: undefinedFunc
2121+ // Backtrace:
2122+ // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
2123+ // undefinedFunc(a);
2124+ // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
2125+ // zzz(x, y, z);
2126+ // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
2127+ // yyy(a, a, a);
2128+ // Line 1 of function script
2129+ // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
2130+ // ...
2131+
2132+ var lines = ex.message.split('\n');
2133+ if (lines.length < 4) {
2134+ return null;
18172135 }
1818− };
18192136
1820− httpData.url = document.location.href;
2137+ var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
2138+ lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i,
2139+ lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
2140+ stack = [],
2141+ scripts = document.getElementsByTagName('script'),
2142+ inlineScriptBlocks = [],
2143+ parts;
18212144
1822− if (document.referrer) {
1823− httpData.headers.Referer = document.referrer;
1824− }
2145+ for (var s in scripts) {
2146+ if (hasKey(scripts, s) && !scripts[s].src) {
2147+ inlineScriptBlocks.push(scripts[s]);
2148+ }
2149+ }
18252150
1826− return httpData;
1827−}
2151+ for (var line = 2; line < lines.length; line += 2) {
2152+ var item = null;
2153+ if ((parts = lineRE1.exec(lines[line]))) {
2154+ item = {
2155+ 'url': parts[2],
2156+ 'func': parts[3],
2157+ 'args': [],
2158+ 'line': +parts[1],
2159+ 'column': null
2160+ };
2161+ } else if ((parts = lineRE2.exec(lines[line]))) {
2162+ item = {
2163+ 'url': parts[3],
2164+ 'func': parts[4],
2165+ 'args': [],
2166+ 'line': +parts[1],
2167+ 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.
2168+ };
2169+ var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block
2170+ var script = inlineScriptBlocks[parts[2] - 1];
2171+ if (script) {
2172+ var source = getSource(item.url);
2173+ if (source) {
2174+ source = source.join('\n');
2175+ var pos = source.indexOf(script.innerText);
2176+ if (pos >= 0) {
2177+ item.line = relativeLine + source.substring(0, pos).split('\n').length;
2178+ }
2179+ }
2180+ }
2181+ } else if ((parts = lineRE3.exec(lines[line]))) {
2182+ var url = window.location.href.replace(/#.*$/, '');
2183+ var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1]));
2184+ var src = findSourceInUrls(re, [url]);
2185+ item = {
2186+ 'url': url,
2187+ 'func': '',
2188+ 'args': [],
2189+ 'line': src ? src.line : parts[1],
2190+ 'column': null
2191+ };
2192+ }
2193+
2194+ if (item) {
2195+ if (!item.func) {
2196+ item.func = guessFunctionName(item.url, item.line);
2197+ }
2198+ var context = gatherContext(item.url, item.line);
2199+ var midline = (context ? context[Math.floor(context.length / 2)] : null);
2200+ if (context && midline.replace(/^\s*/, '') === lines[line + 1].replace(/^\s*/, '')) {
2201+ item.context = context;
2202+ } else {
2203+ // 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);
2204+ item.context = [lines[line + 1]];
2205+ }
2206+ stack.push(item);
2207+ }
2208+ }
18282209
1829−function send(data) {
1830− var baseData = {
1831− project: globalProject,
1832− logger: globalOptions.logger,
1833− platform: 'javascript'
1834− }, httpData = getHttpData();
2210+ if (!stack.length) {
2211+ return null; // could not parse multiline exception message as Opera stack trace
2212+ }
18352213
1836− if (httpData) {
1837− baseData.request = httpData;
2214+ return {
2215+ 'name': ex.name,
2216+ 'message': lines[0],
2217+ 'url': getLocationHref(),
2218+ 'stack': stack
2219+ };
18382220 }
18392221
1840− data = objectMerge(baseData, data);
2222+ /**
2223+ * Adds information about the first frame to incomplete stack traces.
2224+ * Safari and IE require this to get complete data on the first frame.
2225+ * @param {Object.<string, *>} stackInfo Stack trace information from
2226+ * one of the compute* methods.
2227+ * @param {string} url The URL of the script that caused an error.
2228+ * @param {(number|string)} lineNo The line number of the script that
2229+ * caused an error.
2230+ * @param {string=} message The error generated by the browser, which
2231+ * hopefully contains the name of the object that caused the error.
2232+ * @return {boolean} Whether or not the stack information was
2233+ * augmented.
2234+ */
2235+ function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
2236+ var initial = {
2237+ 'url': url,
2238+ 'line': lineNo
2239+ };
18412240
1842− // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
1843− data.tags = objectMerge(objectMerge({}, globalContext.tags), data.tags);
1844− data.extra = objectMerge(objectMerge({}, globalContext.extra), data.extra);
2241+ if (initial.url && initial.line) {
2242+ stackInfo.incomplete = false;
18452243
1846− // Send along our own collected metadata with extra
1847− data.extra['session:duration'] = now() - startTime;
2244+ if (!initial.func) {
2245+ initial.func = guessFunctionName(initial.url, initial.line);
2246+ }
18482247
1849− // If there are no tags/extra, strip the key from the payload alltogther.
1850− if (isEmptyObject(data.tags)) delete data.tags;
2248+ if (!initial.context) {
2249+ initial.context = gatherContext(initial.url, initial.line);
2250+ }
18512251
1852− if (globalContext.user) {
1853− // sentry.interfaces.User
1854− data.user = globalContext.user;
1855− }
2252+ var reference = / '([^']+)' /.exec(message);
2253+ if (reference) {
2254+ initial.column = findSourceInLine(reference[1], initial.url, initial.line);
2255+ }
18562256
1857− // Include the release if it's defined in globalOptions
1858− if (globalOptions.release) data.release = globalOptions.release;
1859− // Include server_name if it's defined in globalOptions
1860− if (globalOptions.serverName) data.server_name = globalOptions.serverName;
2257+ if (stackInfo.stack.length > 0) {
2258+ if (stackInfo.stack[0].url === initial.url) {
2259+ if (stackInfo.stack[0].line === initial.line) {
2260+ return false; // already in stack trace
2261+ } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {
2262+ stackInfo.stack[0].line = initial.line;
2263+ stackInfo.stack[0].context = initial.context;
2264+ return false;
2265+ }
2266+ }
2267+ }
18612268
1862− if (isFunction(globalOptions.dataCallback)) {
1863− data = globalOptions.dataCallback(data) || data;
1864− }
2269+ stackInfo.stack.unshift(initial);
2270+ stackInfo.partial = true;
2271+ return true;
2272+ } else {
2273+ stackInfo.incomplete = true;
2274+ }
18652275
1866− // Why??????????
1867− if (!data || isEmptyObject(data)) {
1868− return;
2276+ return false;
18692277 }
18702278
1871− // Check if the request should be filtered or not
1872− if (isFunction(globalOptions.shouldSendCallback) && !globalOptions.shouldSendCallback(data)) {
1873− return;
1874− }
2279+ /**
2280+ * Computes stack trace information by walking the arguments.caller
2281+ * chain at the time the exception occurred. This will cause earlier
2282+ * frames to be missed but is the only way to get any stack trace in
2283+ * Safari and IE. The top frame is restored by
2284+ * {@link augmentStackTraceWithInitialElement}.
2285+ * @param {Error} ex
2286+ * @return {?Object.<string, *>} Stack trace information.
2287+ */
2288+ function computeStackTraceByWalkingCallerChain(ex, depth) {
2289+ var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,
2290+ stack = [],
2291+ funcs = {},
2292+ recursion = false,
2293+ parts,
2294+ item,
2295+ source;
18752296
1876− // Send along an event_id if not explicitly passed.
1877− // This event_id can be used to reference the error within Sentry itself.
1878− // Set lastEventId after we know the error should actually be sent
1879− lastEventId = data.event_id || (data.event_id = uuid4());
2297+ for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {
2298+ if (curr === computeStackTrace || curr === TraceKit.report) {
2299+ // console.log('skipping internal function');
2300+ continue;
2301+ }
18802302
1881− // Try and clean up the packet before sending by truncating long values
1882− data = trimPacket(data);
2303+ item = {
2304+ 'url': null,
2305+ 'func': UNKNOWN_FUNCTION,
2306+ 'line': null,
2307+ 'column': null
2308+ };
18832309
1884− logDebug('debug', 'Raven about to send:', data);
2310+ if (curr.name) {
2311+ item.func = curr.name;
2312+ } else if ((parts = functionName.exec(curr.toString()))) {
2313+ item.func = parts[1];
2314+ }
18852315
1886− if (!isSetup()) return;
2316+ if (typeof item.func === 'undefined') {
2317+ try {
2318+ item.func = parts.input.substring(0, parts.input.indexOf('{'));
2319+ } catch (e) { }
2320+ }
18872321
1888− (globalOptions.transport || makeRequest)({
1889− url: globalServer,
1890− auth: {
1891− sentry_version: '7',
1892− sentry_client: 'raven-js/' + Raven.VERSION,
1893− sentry_key: globalKey
1894− },
1895− data: data,
1896− options: globalOptions,
1897− onSuccess: function success() {
1898− triggerEvent('success', {
1899− data: data,
1900− src: globalServer
1901− });
1902− },
1903− onError: function failure() {
1904− triggerEvent('failure', {
1905− data: data,
1906− src: globalServer
1907− });
1908− }
1909− });
1910−}
2322+ if ((source = findSourceByFunctionBody(curr))) {
2323+ item.url = source.url;
2324+ item.line = source.line;
19112325
1912−function makeRequest(opts) {
1913− // Tack on sentry_data to auth options, which get urlencoded
1914− opts.auth.sentry_data = JSON.stringify(opts.data);
2326+ if (item.func === UNKNOWN_FUNCTION) {
2327+ item.func = guessFunctionName(item.url, item.line);
2328+ }
19152329
1916− var img = newImage(),
1917− src = opts.url + '?' + urlencode(opts.auth),
1918− crossOrigin = opts.options.crossOrigin;
2330+ var reference = / '([^']+)' /.exec(ex.message || ex.description);
2331+ if (reference) {
2332+ item.column = findSourceInLine(reference[1], source.url, source.line);
2333+ }
2334+ }
19192335
1920− if (crossOrigin || crossOrigin === '') {
1921− img.crossOrigin = crossOrigin;
1922− }
1923− img.onload = opts.onSuccess;
1924− img.onerror = img.onabort = opts.onError;
1925− img.src = src;
1926−}
2336+ if (funcs['' + curr]) {
2337+ recursion = true;
2338+ }else{
2339+ funcs['' + curr] = true;
2340+ }
19272341
1928−// Note: this is shitty, but I can't figure out how to get
1929−// sinon to stub document.createElement without breaking everything
1930−// so this wrapper is just so I can stub it for tests.
1931−function newImage() {
1932− return document.createElement('img');
1933−}
2342+ stack.push(item);
2343+ }
19342344
1935−var ravenNotConfiguredError;
2345+ if (depth) {
2346+ // console.log('depth is ' + depth);
2347+ // console.log('stack is ' + stack.length);
2348+ stack.splice(0, depth);
2349+ }
19362350
1937−function isSetup() {
1938− if (!hasJSON) return false; // needs JSON support
1939− if (!globalServer) {
1940− if (!ravenNotConfiguredError)
1941− logDebug('error', 'Error: Raven has not been configured.');
1942− ravenNotConfiguredError = true;
1943− return false;
2351+ var result = {
2352+ 'name': ex.name,
2353+ 'message': ex.message,
2354+ 'url': getLocationHref(),
2355+ 'stack': stack
2356+ };
2357+ augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);
2358+ return result;
19442359 }
1945− return true;
1946−}
19472360
1948−function joinRegExp(patterns) {
1949− // Combine an array of regular expressions and strings into one large regexp
1950− // Be mad.
1951− var sources = [],
1952− i = 0, len = patterns.length,
1953− pattern;
2361+ /**
2362+ * Computes a stack trace for an exception.
2363+ * @param {Error} ex
2364+ * @param {(string|number)=} depth
2365+ */
2366+ function computeStackTrace(ex, depth) {
2367+ var stack = null;
2368+ depth = (depth == null ? 0 : +depth);
19542369
1955− for (; i < len; i++) {
1956− pattern = patterns[i];
1957− if (isString(pattern)) {
1958− // If it's a string, we need to escape it
1959− // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
1960− sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"));
1961− } else if (pattern && pattern.source) {
1962− // If it's a regexp already, we want to extract the source
1963− sources.push(pattern.source);
2370+ try {
2371+ // This must be tried first because Opera 10 *destroys*
2372+ // its stacktrace property if you try to access the stack
2373+ // property first!!
2374+ stack = computeStackTraceFromStacktraceProp(ex);
2375+ if (stack) {
2376+ return stack;
2377+ }
2378+ } catch (e) {
2379+ if (TraceKit.debug) {
2380+ throw e;
2381+ }
19642382 }
1965− // Intentionally skip other cases
1966− }
1967− return new RegExp(sources.join('|'), 'i');
1968−}
1969−
1970−function uuid4() {
1971− var crypto = window.crypto || window.msCrypto;
1972−
1973− if (!isUndefined(crypto) && crypto.getRandomValues) {
1974− // Use window.crypto API if available
1975− var arr = new Uint16Array(8);
1976− crypto.getRandomValues(arr);
1977−
1978− // set 4 in byte 7
1979− arr[3] = arr[3] & 0xFFF | 0x4000;
1980− // set 2 most significant bits of byte 9 to '10'
1981− arr[4] = arr[4] & 0x3FFF | 0x8000;
19822383
1983− var pad = function(num) {
1984− var v = num.toString(16);
1985− while (v.length < 4) {
1986− v = '0' + v;
2384+ try {
2385+ stack = computeStackTraceFromStackProp(ex);
2386+ if (stack) {
2387+ return stack;
19872388 }
1988− return v;
1989− };
1990−
1991− return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
1992− pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
1993− } else {
1994− // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
1995− return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1996− var r = Math.random()*16|0,
1997− v = c == 'x' ? r : (r&0x3|0x8);
1998− return v.toString(16);
1999− });
2000− }
2001−}
2002−
2003−function logDebug(level) {
2004− if (originalConsoleMethods[level] && Raven.debug) {
2005− // _slice is coming from vendor/TraceKit/tracekit.js
2006− // so it's accessible globally
2007− originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1));
2008− }
2009−}
2389+ } catch (e) {
2390+ if (TraceKit.debug) {
2391+ throw e;
2392+ }
2393+ }
20102394
2011−function afterLoad() {
2012− // Attempt to initialize Raven on load
2013− var RavenConfig = window.RavenConfig;
2014− if (RavenConfig) {
2015− Raven.config(RavenConfig.dsn, RavenConfig.config).install();
2016− }
2017−}
2395+ try {
2396+ stack = computeStackTraceFromOperaMultiLineMessage(ex);
2397+ if (stack) {
2398+ return stack;
2399+ }
2400+ } catch (e) {
2401+ if (TraceKit.debug) {
2402+ throw e;
2403+ }
2404+ }
20182405
2019−function urlencode(o) {
2020− var pairs = [];
2021− each(o, function(key, value) {
2022− pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
2023− });
2024− return pairs.join('&');
2025−}
2406+ try {
2407+ stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);
2408+ if (stack) {
2409+ return stack;
2410+ }
2411+ } catch (e) {
2412+ if (TraceKit.debug) {
2413+ throw e;
2414+ }
2415+ }
20262416
2027−function mergeContext(key, context) {
2028− if (isUndefined(context)) {
2029− delete globalContext[key];
2030− } else {
2031− globalContext[key] = objectMerge(globalContext[key] || {}, context);
2417+ return {
2418+ 'name': ex.name,
2419+ 'message': ex.message,
2420+ 'url': getLocationHref()
2421+ };
20322422 }
2033−}
20342423
2035−afterLoad();
2424+ computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;
2425+ computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp;
2426+ computeStackTrace.guessFunctionName = guessFunctionName;
2427+ computeStackTrace.gatherContext = gatherContext;
20362428
2037−// This is being exposed no matter what because there are too many weird
2038−// usecases for how people use Raven. If this is really a problem, I'm sorry.
2039−window.Raven = Raven;
2429+ return computeStackTrace;
2430+}());
20402431
2041−// Expose Raven to the world
2042−if (typeof define === 'function' && define.amd) {
2043− // AMD
2044− define('raven', [], function() {
2045− return Raven;
2046− });
2047−} else if (typeof module === 'object') {
2048− // browserify
2049− module.exports = Raven;
2050−} else if (typeof exports === 'object') {
2051− // CommonJS
2052− exports = Raven;
2053−}
2432+module.exports = TraceKit;
20542433
2055−})(typeof window !== 'undefined' ? window : this);
2434+},{"4":4}]},{},[3])(3)
2435+});