History

Update Raven.js

Thibaut Courouble committed 8 years ago

Commit 4dc85f81 parent 0bd82891 file · +2544 −2655Browse files

Unified · Split

assets/javascripts/vendor/raven.js+2544−2655
@@ -1,4 +1,4 @@
1/*! Raven.js 3.18.0 (b771bb2) | github.com/getsentry/raven-js */1/*! Raven.js 3.20.1 (42adaf5) | github.com/getsentry/raven-js */
2 2
3/*3/*
4 * Includes TraceKit4 * Includes TraceKit
@@ -10,322 +10,270 @@ · 5 unmodified lines
10 *10 *
11 */11 */
12 12
13(function(f) {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){
14 if (typeof exports === 'object' && typeof module !== 'undefined') {14function RavenConfigError(message) {
15 module.exports = f();15 this.name = 'RavenConfigError';
16 } else if (typeof define === 'function' && define.amd) {16 this.message = message;
17 define([], f);17}
18 } else {18RavenConfigError.prototype = new Error();
19 var g;19RavenConfigError.prototype.constructor = RavenConfigError;
20 if (typeof window !== 'undefined') {
21 g = window;
22 } else if (typeof global !== 'undefined') {
23 g = global;
24 } else if (typeof self !== 'undefined') {
25 g = self;
26 } else {
27 g = this;
28 }
29 g.Raven = f();
30 }
31})(function() {
32 var define, module, exports;
33 return (function e(t, n, r) {
34 function s(o, u) {
35 if (!n[o]) {
36 if (!t[o]) {
37 var a = typeof require == 'function' && require;
38 if (!u && a) return a(o, !0);
39 if (i) return i(o, !0);
40 var f = new Error("Cannot find module '" + o + "'");
41 throw ((f.code = 'MODULE_NOT_FOUND'), f);
42 }
43 var l = (n[o] = {exports: {}});
44 t[o][0].call(
45 l.exports,
46 function(e) {
47 var n = t[o][1][e];
48 return s(n ? n : e);
49 },
50 l,
51 l.exports,
52 e,
53 t,
54 n,
55 r
56 );
57 }
58 return n[o].exports;
59 }
60 var i = typeof require == 'function' && require;
61 for (var o = 0; o < r.length; o++) s(r[o]);
62 return s;
63 })(
64 {
65 1: [
66 function(_dereq_, module, exports) {
67 function RavenConfigError(message) {
68 this.name = 'RavenConfigError';
69 this.message = message;
70 }
71 RavenConfigError.prototype = new Error();
72 RavenConfigError.prototype.constructor = RavenConfigError;
73
74 module.exports = RavenConfigError;
75 },
76 {}
77 ],
78 2: [
79 function(_dereq_, module, exports) {
80 var wrapMethod = function(console, level, callback) {
81 var originalConsoleLevel = console[level];
82 var originalConsole = console;
83
84 if (!(level in console)) {
85 return;
86 }
87 20
88 var sentryLevel = level === 'warn' ? 'warning' : level;21module.exports = RavenConfigError;
89
90 console[level] = function() {
91 var args = [].slice.call(arguments);
92
93 var msg = '' + args.join(' ');
94 var data = {
95 level: sentryLevel,
96 logger: 'console',
97 extra: {arguments: args}
98 };
99
100 if (level === 'assert') {
101 if (args[0] === false) {
102 // Default browsers message
103 msg =
104 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert');
105 data.extra.arguments = args.slice(1);
106 callback && callback(msg, data);
107 }
108 } else {
109 callback && callback(msg, data);
110 }
111
112 // this fails for some browsers. :(
113 if (originalConsoleLevel) {
114 // IE9 doesn't allow calling apply on console functions directly
115 // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
116 Function.prototype.apply.call(
117 originalConsoleLevel,
118 originalConsole,
119 args
120 );
121 }
122 };
123 };
124
125 module.exports = {
126 wrapMethod: wrapMethod
127 };
128 },
129 {}
130 ],
131 3: [
132 function(_dereq_, module, exports) {
133 (function(global) {
134 /*global XDomainRequest:false, __DEV__:false*/
135 22
136 var TraceKit = _dereq_(6);23},{}],2:[function(_dereq_,module,exports){
137 var stringify = _dereq_(7);24var wrapMethod = function(console, level, callback) {
138 var RavenConfigError = _dereq_(1);25 var originalConsoleLevel = console[level];
139 var utils = _dereq_(5);26 var originalConsole = console;
140 27
141 var isError = utils.isError,28 if (!(level in console)) {
142 isObject = utils.isObject;29 return;
30 }
143 31
144 var wrapConsoleMethod = _dereq_(2).wrapMethod;32 var sentryLevel = level === 'warn' ? 'warning' : level;
145 33
146 var dsnKeys = 'source protocol user pass host port path'.split(' '),34 console[level] = function() {
147 dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;35 var args = [].slice.call(arguments);
148 36
149 function now() {37 var msg = '' + args.join(' ');
150 return +new Date();38 var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}};
151 }
152 39
153 // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)40 if (level === 'assert') {
154 var _window =41 if (args[0] === false) {
155 typeof window !== 'undefined'42 // Default browsers message
156 ? window43 msg = 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert');
157 : typeof global !== 'undefined'44 data.extra.arguments = args.slice(1);
158 ? global45 callback && callback(msg, data);
159 : typeof self !== 'undefined' ? self : {};46 }
160 var _document = _window.document;47 } else {
161 var _navigator = _window.navigator;48 callback && callback(msg, data);
162 49 }
163 function keepOriginalCallback(original, callback) {
164 return isFunction(callback)
165 ? function(data) {
166 return callback(data, original);
167 }
168 : callback;
169 }
170 50
171 // First, check for JSON support51 // this fails for some browsers. :(
172 // If there is no JSON, we no-op the core features of Raven52 if (originalConsoleLevel) {
173 // since JSON is required to encode the payload53 // IE9 doesn't allow calling apply on console functions directly
174 function Raven() {54 // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
175 this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);55 Function.prototype.apply.call(originalConsoleLevel, originalConsole, args);
176 // Raven can run in contexts where there's no document (react-native)56 }
177 this._hasDocument = !isUndefined(_document);57 };
178 this._hasNavigator = !isUndefined(_navigator);58};
179 this._lastCapturedException = null;59
180 this._lastData = null;60module.exports = {
181 this._lastEventId = null;61 wrapMethod: wrapMethod
182 this._globalServer = null;62};
183 this._globalKey = null;63
184 this._globalProject = null;64},{}],3:[function(_dereq_,module,exports){
185 this._globalContext = {};65(function (global){
186 this._globalOptions = {66/*global XDomainRequest:false */
187 logger: 'javascript',67
188 ignoreErrors: [],68var TraceKit = _dereq_(6);
189 ignoreUrls: [],69var stringify = _dereq_(7);
190 whitelistUrls: [],70var RavenConfigError = _dereq_(1);
191 includePaths: [],71
192 collectWindowErrors: true,72var utils = _dereq_(5);
193 maxMessageLength: 0,73var isError = utils.isError;
194 74var isObject = utils.isObject;
195 // By default, truncates URL values to 250 chars75var isErrorEvent = utils.isErrorEvent;
196 maxUrlLength: 250,76var isUndefined = utils.isUndefined;
197 stackTraceLimit: 50,77var isFunction = utils.isFunction;
198 autoBreadcrumbs: true,78var isString = utils.isString;
199 instrument: true,79var isArray = utils.isArray;
200 sampleRate: 180var isEmptyObject = utils.isEmptyObject;
201 };81var each = utils.each;
202 this._ignoreOnError = 0;82var objectMerge = utils.objectMerge;
203 this._isRavenInstalled = false;83var truncate = utils.truncate;
204 this._originalErrorStackTraceLimit = Error.stackTraceLimit;84var objectFrozen = utils.objectFrozen;
205 // capture references to window.console *and* all its methods first85var hasKey = utils.hasKey;
206 // before the console plugin has a chance to monkey patch86var joinRegExp = utils.joinRegExp;
207 this._originalConsole = _window.console || {};87var urlencode = utils.urlencode;
208 this._originalConsoleMethods = {};88var uuid4 = utils.uuid4;
209 this._plugins = [];89var htmlTreeAsString = utils.htmlTreeAsString;
210 this._startTime = now();90var isSameException = utils.isSameException;
211 this._wrappedBuiltIns = [];91var isSameStacktrace = utils.isSameStacktrace;
212 this._breadcrumbs = [];92var parseUrl = utils.parseUrl;
213 this._lastCapturedEvent = null;93var fill = utils.fill;
214 this._keypressTimeout;94
215 this._location = _window.location;95var wrapConsoleMethod = _dereq_(2).wrapMethod;
216 this._lastHref = this._location && this._location.href;96
217 this._resetBackoff();97var dsnKeys = 'source protocol user pass host port path'.split(' '),
218 98 dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
219 // eslint-disable-next-line guard-for-in99
220 for (var method in this._originalConsole) {100function now() {
221 this._originalConsoleMethods[method] = this._originalConsole[method];101 return +new Date();
222 }102}
223 }103
104// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
105var _window =
106 typeof window !== 'undefined'
107 ? window
108 : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
109var _document = _window.document;
110var _navigator = _window.navigator;
111
112function keepOriginalCallback(original, callback) {
113 return isFunction(callback)
114 ? function(data) {
115 return callback(data, original);
116 }
117 : callback;
118}
119
120// First, check for JSON support
121// If there is no JSON, we no-op the core features of Raven
122// since JSON is required to encode the payload
123function Raven() {
124 this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
125 // Raven can run in contexts where there's no document (react-native)
126 this._hasDocument = !isUndefined(_document);
127 this._hasNavigator = !isUndefined(_navigator);
128 this._lastCapturedException = null;
129 this._lastData = null;
130 this._lastEventId = null;
131 this._globalServer = null;
132 this._globalKey = null;
133 this._globalProject = null;
134 this._globalContext = {};
135 this._globalOptions = {
136 logger: 'javascript',
137 ignoreErrors: [],
138 ignoreUrls: [],
139 whitelistUrls: [],
140 includePaths: [],
141 collectWindowErrors: true,
142 maxMessageLength: 0,
143
144 // By default, truncates URL values to 250 chars
145 maxUrlLength: 250,
146 stackTraceLimit: 50,
147 autoBreadcrumbs: true,
148 instrument: true,
149 sampleRate: 1
150 };
151 this._ignoreOnError = 0;
152 this._isRavenInstalled = false;
153 this._originalErrorStackTraceLimit = Error.stackTraceLimit;
154 // capture references to window.console *and* all its methods first
155 // before the console plugin has a chance to monkey patch
156 this._originalConsole = _window.console || {};
157 this._originalConsoleMethods = {};
158 this._plugins = [];
159 this._startTime = now();
160 this._wrappedBuiltIns = [];
161 this._breadcrumbs = [];
162 this._lastCapturedEvent = null;
163 this._keypressTimeout;
164 this._location = _window.location;
165 this._lastHref = this._location && this._location.href;
166 this._resetBackoff();
167
168 // eslint-disable-next-line guard-for-in
169 for (var method in this._originalConsole) {
170 this._originalConsoleMethods[method] = this._originalConsole[method];
171 }
172}
224 173
225 /*174/*
226 * The core Raven singleton175 * The core Raven singleton
227 *176 *
228 * @this {Raven}177 * @this {Raven}
229 */178 */
230 179
231 Raven.prototype = {180Raven.prototype = {
232 // Hardcode version string so that raven source can be loaded directly via181 // Hardcode version string so that raven source can be loaded directly via
233 // webpack (using a build step causes webpack #1617). Grunt verifies that182 // webpack (using a build step causes webpack #1617). Grunt verifies that
234 // this value matches package.json during build.183 // this value matches package.json during build.
235 // See: https://github.com/getsentry/raven-js/issues/465184 // See: https://github.com/getsentry/raven-js/issues/465
236 VERSION: '3.18.0',185 VERSION: '3.20.1',
237 186
238 debug: false,187 debug: false,
239 188
240 TraceKit: TraceKit, // alias to TraceKit189 TraceKit: TraceKit, // alias to TraceKit
241 190
242 /*191 /*
243 * Configure Raven with a DSN and extra options192 * Configure Raven with a DSN and extra options
244 *193 *
245 * @param {string} dsn The public Sentry DSN194 * @param {string} dsn The public Sentry DSN
246 * @param {object} options Set of global options [optional]195 * @param {object} options Set of global options [optional]
247 * @return {Raven}196 * @return {Raven}
248 */197 */
249 config: function(dsn, options) {198 config: function(dsn, options) {
250 var self = this;199 var self = this;
251
252 if (self._globalServer) {
253 this._logDebug('error', 'Error: Raven has already been configured');
254 return self;
255 }
256 if (!dsn) return self;
257
258 var globalOptions = self._globalOptions;
259
260 // merge in options
261 if (options) {
262 each(options, function(key, value) {
263 // tags and extra are special and need to be put into context
264 if (key === 'tags' || key === 'extra' || key === 'user') {
265 self._globalContext[key] = value;
266 } else {
267 globalOptions[key] = value;
268 }
269 });
270 }
271 200
272 self.setDSN(dsn);201 if (self._globalServer) {
273 202 this._logDebug('error', 'Error: Raven has already been configured');
274 // "Script error." is hard coded into browsers for errors that it can't read.203 return self;
275 // this is the result of a script being pulled in from an external domain and CORS.204 }
276 globalOptions.ignoreErrors.push(/^Script error\.?$/);205 if (!dsn) return self;
277 globalOptions.ignoreErrors.push(206
278 /^Javascript error: Script error\.? on line 0$/207 var globalOptions = self._globalOptions;
279 );208
280 209 // merge in options
281 // join regexp rules into one big rule210 if (options) {
282 globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);211 each(options, function(key, value) {
283 globalOptions.ignoreUrls = globalOptions.ignoreUrls.length212 // tags and extra are special and need to be put into context
284 ? joinRegExp(globalOptions.ignoreUrls)213 if (key === 'tags' || key === 'extra' || key === 'user') {
285 : false;214 self._globalContext[key] = value;
286 globalOptions.whitelistUrls = globalOptions.whitelistUrls.length215 } else {
287 ? joinRegExp(globalOptions.whitelistUrls)216 globalOptions[key] = value;
288 : false;217 }
289 globalOptions.includePaths = joinRegExp(globalOptions.includePaths);218 });
290 globalOptions.maxBreadcrumbs = Math.max(219 }
291 0,
292 Math.min(globalOptions.maxBreadcrumbs || 100, 100)
293 ); // default and hard limit is 100
294
295 var autoBreadcrumbDefaults = {
296 xhr: true,
297 console: true,
298 dom: true,
299 location: true
300 };
301 220
302 var autoBreadcrumbs = globalOptions.autoBreadcrumbs;221 self.setDSN(dsn);
303 if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {222
304 autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);223 // "Script error." is hard coded into browsers for errors that it can't read.
305 } else if (autoBreadcrumbs !== false) {224 // this is the result of a script being pulled in from an external domain and CORS.
306 autoBreadcrumbs = autoBreadcrumbDefaults;225 globalOptions.ignoreErrors.push(/^Script error\.?$/);
307 }226 globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
308 globalOptions.autoBreadcrumbs = autoBreadcrumbs;227
228 // join regexp rules into one big rule
229 globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
230 globalOptions.ignoreUrls = globalOptions.ignoreUrls.length
231 ? joinRegExp(globalOptions.ignoreUrls)
232 : false;
233 globalOptions.whitelistUrls = globalOptions.whitelistUrls.length
234 ? joinRegExp(globalOptions.whitelistUrls)
235 : false;
236 globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
237 globalOptions.maxBreadcrumbs = Math.max(
238 0,
239 Math.min(globalOptions.maxBreadcrumbs || 100, 100)
240 ); // default and hard limit is 100
241
242 var autoBreadcrumbDefaults = {
243 xhr: true,
244 console: true,
245 dom: true,
246 location: true,
247 sentry: true
248 };
249
250 var autoBreadcrumbs = globalOptions.autoBreadcrumbs;
251 if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {
252 autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);
253 } else if (autoBreadcrumbs !== false) {
254 autoBreadcrumbs = autoBreadcrumbDefaults;
255 }
256 globalOptions.autoBreadcrumbs = autoBreadcrumbs;
309 257
310 var instrumentDefaults = {258 var instrumentDefaults = {
311 tryCatch: true259 tryCatch: true
312 };260 };
313 261
314 var instrument = globalOptions.instrument;262 var instrument = globalOptions.instrument;
315 if ({}.toString.call(instrument) === '[object Object]') {263 if ({}.toString.call(instrument) === '[object Object]') {
316 instrument = objectMerge(instrumentDefaults, instrument);264 instrument = objectMerge(instrumentDefaults, instrument);
317 } else if (instrument !== false) {265 } else if (instrument !== false) {
318 instrument = instrumentDefaults;266 instrument = instrumentDefaults;
319 }267 }
320 globalOptions.instrument = instrument;268 globalOptions.instrument = instrument;
321 269
322 TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;270 TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
323 271
324 // return for chaining272 // return for chaining
325 return self;273 return self;
326 },274 },
327 275
328 /*276 /*
329 * Installs a global window.onerror error handler277 * Installs a global window.onerror error handler
330 * to capture and report uncaught exceptions.278 * to capture and report uncaught exceptions.
331 * At this point, install() is required to be called due279 * At this point, install() is required to be called due
@@ -333,63 +281,58 @@ · 1 unmodified line
333 *281 *
334 * @return {Raven}282 * @return {Raven}
335 */283 */
336 install: function() {284 install: function() {
337 var self = this;285 var self = this;
338 if (self.isSetup() && !self._isRavenInstalled) {286 if (self.isSetup() && !self._isRavenInstalled) {
339 TraceKit.report.subscribe(function() {287 TraceKit.report.subscribe(function() {
340 self._handleOnErrorStackInfo.apply(self, arguments);288 self._handleOnErrorStackInfo.apply(self, arguments);
341 });289 });
342 if (
343 self._globalOptions.instrument &&
344 self._globalOptions.instrument.tryCatch
345 ) {
346 self._instrumentTryCatch();
347 }
348 290
349 if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs();291 self._patchFunctionToString();
350 292
351 // Install all of the plugins293 if (self._globalOptions.instrument && self._globalOptions.instrument.tryCatch) {
352 self._drainPlugins();294 self._instrumentTryCatch();
295 }
353 296
354 self._isRavenInstalled = true;297 if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs();
355 }
356 298
357 Error.stackTraceLimit = self._globalOptions.stackTraceLimit;299 // Install all of the plugins
358 return this;300 self._drainPlugins();
359 },
360 301
361 /*302 self._isRavenInstalled = true;
303 }
304
305 Error.stackTraceLimit = self._globalOptions.stackTraceLimit;
306 return this;
307 },
308
309 /*
362 * Set the DSN (can be called multiple time unlike config)310 * Set the DSN (can be called multiple time unlike config)
363 *311 *
364 * @param {string} dsn The public Sentry DSN312 * @param {string} dsn The public Sentry DSN
365 */313 */
366 setDSN: function(dsn) {314 setDSN: function(dsn) {
367 var self = this,315 var self = this,
368 uri = self._parseDSN(dsn),316 uri = self._parseDSN(dsn),
369 lastSlash = uri.path.lastIndexOf('/'),317 lastSlash = uri.path.lastIndexOf('/'),
370 path = uri.path.substr(1, lastSlash);318 path = uri.path.substr(1, lastSlash);
371 319
372 self._dsn = dsn;320 self._dsn = dsn;
373 self._globalKey = uri.user;321 self._globalKey = uri.user;
374 self._globalSecret = uri.pass && uri.pass.substr(1);322 self._globalSecret = uri.pass && uri.pass.substr(1);
375 self._globalProject = uri.path.substr(lastSlash + 1);323 self._globalProject = uri.path.substr(lastSlash + 1);
376 324
377 self._globalServer = self._getGlobalServer(uri);325 self._globalServer = self._getGlobalServer(uri);
378 326
379 self._globalEndpoint =327 self._globalEndpoint =
380 self._globalServer +328 self._globalServer + '/' + path + 'api/' + self._globalProject + '/store/';
381 '/' +329
382 path +330 // Reset backoff state since we may be pointing at a
383 'api/' +331 // new project/server
384 self._globalProject +332 this._resetBackoff();
385 '/store/';333 },
386 334
387 // Reset backoff state since we may be pointing at a335 /*
388 // new project/server
389 this._resetBackoff();
390 },
391
392 /*
393 * Wrap code within a context so Raven can capture errors336 * Wrap code within a context so Raven can capture errors
394 * reliably across domains that is executed immediately.337 * reliably across domains that is executed immediately.
395 *338 *
@@ -397,17 +340,17 @@ · 1 unmodified line
397 * @param {function} func The callback to be immediately executed within the context340 * @param {function} func The callback to be immediately executed within the context
398 * @param {array} args An array of arguments to be called with the callback [optional]341 * @param {array} args An array of arguments to be called with the callback [optional]
399 */342 */
400 context: function(options, func, args) {343 context: function(options, func, args) {
401 if (isFunction(options)) {344 if (isFunction(options)) {
402 args = func || [];345 args = func || [];
403 func = options;346 func = options;
404 options = undefined;347 options = undefined;
405 }348 }
406 349
407 return this.wrap(options, func).apply(this, args);350 return this.wrap(options, func).apply(this, args);
408 },351 },
409 352
410 /*353 /*
411 * Wrap code within a context and returns back a new function to be executed354 * Wrap code within a context and returns back a new function to be executed
412 *355 *
413 * @param {object} options A specific set of options for this context [optional]356 * @param {object} options A specific set of options for this context [optional]
@@ -415,381 +358,397 @@ · 1 unmodified line
415 * @param {function} func A function to call before the try/catch wrapper [optional, private]358 * @param {function} func A function to call before the try/catch wrapper [optional, private]
416 * @return {function} The newly wrapped functions with a context359 * @return {function} The newly wrapped functions with a context
417 */360 */
418 wrap: function(options, func, _before) {361 wrap: function(options, func, _before) {
419 var self = this;362 var self = this;
420 // 1 argument has been passed, and it's not a function363 // 1 argument has been passed, and it's not a function
421 // so just return it364 // so just return it
422 if (isUndefined(func) && !isFunction(options)) {365 if (isUndefined(func) && !isFunction(options)) {
423 return options;366 return options;
424 }367 }
425
426 // options is optional
427 if (isFunction(options)) {
428 func = options;
429 options = undefined;
430 }
431 368
432 // At this point, we've passed along 2 arguments, and the second one369 // options is optional
433 // is not a function either, so we'll just return the second argument.370 if (isFunction(options)) {
434 if (!isFunction(func)) {371 func = options;
435 return func;372 options = undefined;
436 }373 }
437 374
438 // We don't wanna wrap it twice!375 // At this point, we've passed along 2 arguments, and the second one
439 try {376 // is not a function either, so we'll just return the second argument.
440 if (func.__raven__) {377 if (!isFunction(func)) {
441 return func;378 return func;
442 }379 }
443 380
444 // If this has already been wrapped in the past, return that381 // We don't wanna wrap it twice!
445 if (func.__raven_wrapper__) {382 try {
446 return func.__raven_wrapper__;383 if (func.__raven__) {
447 }384 return func;
448 } catch (e) {385 }
449 // Just accessing custom props in some Selenium environments
450 // can cause a "Permission denied" exception (see raven-js#495).
451 // Bail on wrapping and return the function as-is (defers to window.onerror).
452 return func;
453 }
454 386
455 function wrapped() {387 // If this has already been wrapped in the past, return that
456 var args = [],388 if (func.__raven_wrapper__) {
457 i = arguments.length,389 return func.__raven_wrapper__;
458 deep = !options || (options && options.deep !== false);390 }
391 } catch (e) {
392 // Just accessing custom props in some Selenium environments
393 // can cause a "Permission denied" exception (see raven-js#495).
394 // Bail on wrapping and return the function as-is (defers to window.onerror).
395 return func;
396 }
459 397
460 if (_before && isFunction(_before)) {398 function wrapped() {
461 _before.apply(this, arguments);399 var args = [],
462 }400 i = arguments.length,
401 deep = !options || (options && options.deep !== false);
463 402
464 // Recursively wrap all of a function's arguments that are403 if (_before && isFunction(_before)) {
465 // functions themselves.404 _before.apply(this, arguments);
466 while (i--)405 }
467 args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
468 406
469 try {407 // Recursively wrap all of a function's arguments that are
470 // Attempt to invoke user-land function408 // functions themselves.
471 // NOTE: If you are a Sentry user, and you are seeing this stack frame, it409 while (i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
472 // means Raven caught an error invoking your application code. This is410
473 // expected behavior and NOT indicative of a bug with Raven.js.411 try {
474 return func.apply(this, args);412 // Attempt to invoke user-land function
475 } catch (e) {413 // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
476 self._ignoreNextOnError();414 // means Raven caught an error invoking your application code. This is
477 self.captureException(e, options);415 // expected behavior and NOT indicative of a bug with Raven.js.
478 throw e;416 return func.apply(this, args);
479 }417 } catch (e) {
480 }418 self._ignoreNextOnError();
419 self.captureException(e, options);
420 throw e;
421 }
422 }
481 423
482 // copy over properties of the old function424 // copy over properties of the old function
483 for (var property in func) {425 for (var property in func) {
484 if (hasKey(func, property)) {426 if (hasKey(func, property)) {
485 wrapped[property] = func[property];427 wrapped[property] = func[property];
486 }428 }
487 }429 }
488 wrapped.prototype = func.prototype;430 wrapped.prototype = func.prototype;
489 431
490 func.__raven_wrapper__ = wrapped;432 func.__raven_wrapper__ = wrapped;
491 // Signal that this function has been wrapped already433 // Signal that this function has been wrapped/filled already
492 // for both debugging and to prevent it to being wrapped twice434 // for both debugging and to prevent it to being wrapped/filled twice
493 wrapped.__raven__ = true;435 wrapped.__raven__ = true;
494 wrapped.__inner__ = func;436 wrapped.__orig__ = func;
495 437
496 return wrapped;438 return wrapped;
497 },439 },
498 440
499 /*441 /*
500 * Uninstalls the global error handler.442 * Uninstalls the global error handler.
501 *443 *
502 * @return {Raven}444 * @return {Raven}
503 */445 */
504 uninstall: function() {446 uninstall: function() {
505 TraceKit.report.uninstall();447 TraceKit.report.uninstall();
506 448
507 this._restoreBuiltIns();449 this._unpatchFunctionToString();
450 this._restoreBuiltIns();
508 451
509 Error.stackTraceLimit = this._originalErrorStackTraceLimit;452 Error.stackTraceLimit = this._originalErrorStackTraceLimit;
510 this._isRavenInstalled = false;453 this._isRavenInstalled = false;
511 454
512 return this;455 return this;
513 },456 },
514 457
515 /*458 /*
516 * Manually capture an exception and send it over to Sentry459 * Manually capture an exception and send it over to Sentry
517 *460 *
518 * @param {error} ex An exception to be logged461 * @param {error} ex An exception to be logged
519 * @param {object} options A specific set of options for this error [optional]462 * @param {object} options A specific set of options for this error [optional]
520 * @return {Raven}463 * @return {Raven}
521 */464 */
522 captureException: function(ex, options) {465 captureException: function(ex, options) {
523 // If not an Error is passed through, recall as a message instead466 // Cases for sending ex as a message, rather than an exception
524 if (!isError(ex)) {467 var isNotError = !isError(ex);
525 return this.captureMessage(468 var isNotErrorEvent = !isErrorEvent(ex);
526 ex,469 var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error;
527 objectMerge(470
528 {471 if ((isNotError && isNotErrorEvent) || isErrorEventWithoutError) {
529 trimHeadFrames: 1,472 return this.captureMessage(
530 stacktrace: true // if we fall back to captureMessage, default to attempting a new trace473 ex,
531 },474 objectMerge(
532 options475 {
533 )476 trimHeadFrames: 1,
534 );477 stacktrace: true // if we fall back to captureMessage, default to attempting a new trace
535 }478 },
536 479 options
537 // Store the raw exception object for potential debugging and introspection480 )
538 this._lastCapturedException = ex;481 );
482 }
539 483
540 // TraceKit.report will re-raise any exception passed to it,484 // Get actual Error from ErrorEvent
541 // which means you have to wrap it in try/catch. Instead, we485 if (isErrorEvent(ex)) ex = ex.error;
542 // can wrap it here and only re-raise if TraceKit.report486
543 // raises an exception different from the one we asked to487 // Store the raw exception object for potential debugging and introspection
544 // report on.488 this._lastCapturedException = ex;
545 try {489
546 var stack = TraceKit.computeStackTrace(ex);490 // TraceKit.report will re-raise any exception passed to it,
547 this._handleStackInfo(stack, options);491 // which means you have to wrap it in try/catch. Instead, we
548 } catch (ex1) {492 // can wrap it here and only re-raise if TraceKit.report
549 if (ex !== ex1) {493 // raises an exception different from the one we asked to
550 throw ex1;494 // report on.
551 }495 try {
552 }496 var stack = TraceKit.computeStackTrace(ex);
497 this._handleStackInfo(stack, options);
498 } catch (ex1) {
499 if (ex !== ex1) {
500 throw ex1;
501 }
502 }
553 503
554 return this;504 return this;
555 },505 },
556 506
557 /*507 /*
558 * Manually send a message to Sentry508 * Manually send a message to Sentry
559 *509 *
560 * @param {string} msg A plain message to be captured in Sentry510 * @param {string} msg A plain message to be captured in Sentry
561 * @param {object} options A specific set of options for this message [optional]511 * @param {object} options A specific set of options for this message [optional]
562 * @return {Raven}512 * @return {Raven}
563 */513 */
564 captureMessage: function(msg, options) {514 captureMessage: function(msg, options) {
565 // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an515 // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
566 // early call; we'll error on the side of logging anything called before configuration since it's516 // early call; we'll error on the side of logging anything called before configuration since it's
567 // probably something you should see:517 // probably something you should see:
568 if (518 if (
569 !!this._globalOptions.ignoreErrors.test &&519 !!this._globalOptions.ignoreErrors.test &&
570 this._globalOptions.ignoreErrors.test(msg)520 this._globalOptions.ignoreErrors.test(msg)
571 ) {521 ) {
572 return;522 return;
573 }523 }
574 524
575 options = options || {};525 options = options || {};
576 526
577 var data = objectMerge(527 var data = objectMerge(
578 {528 {
579 message: msg + '' // Make sure it's actually a string529 message: msg + '' // Make sure it's actually a string
580 },530 },
581 options531 options
582 );532 );
583 533
584 if (this._globalOptions.stacktrace || (options && options.stacktrace)) {534 var ex;
585 var ex;535 // Generate a "synthetic" stack trace from this point.
586 // Generate a "synthetic" stack trace from this point.536 // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
587 // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative537 // of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
588 // of a bug with Raven.js. Sentry generates synthetic traces either by configuration,538 // or if it catches a thrown object without a "stack" property.
589 // or if it catches a thrown object without a "stack" property.539 try {
590 try {540 throw new Error(msg);
591 throw new Error(msg);541 } catch (ex1) {
592 } catch (ex1) {542 ex = ex1;
593 ex = ex1;543 }
594 }
595 544
596 // null exception name so `Error` isn't prefixed to msg545 // null exception name so `Error` isn't prefixed to msg
597 ex.name = null;546 ex.name = null;
598 547 var stack = TraceKit.computeStackTrace(ex);
599 options = objectMerge(548
600 {549 // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
601 // fingerprint on msg, not stack trace (legacy behavior, could be550 var initialCall = isArray(stack.stack) && stack.stack[1];
602 // revisited)551 var fileurl = (initialCall && initialCall.url) || '';
603 fingerprint: msg,552
604 // since we know this is a synthetic trace, the top N-most frames553 if (
605 // MUST be from Raven.js, so mark them as in_app later by setting554 !!this._globalOptions.ignoreUrls.test &&
606 // trimHeadFrames555 this._globalOptions.ignoreUrls.test(fileurl)
607 trimHeadFrames: (options.trimHeadFrames || 0) + 1556 ) {
608 },557 return;
609 options558 }
610 );
611
612 var stack = TraceKit.computeStackTrace(ex);
613 var frames = this._prepareFrames(stack, options);
614 data.stacktrace = {
615 // Sentry expects frames oldest to newest
616 frames: frames.reverse()
617 };
618 }
619 559
620 // Fire away!560 if (
621 this._send(data);561 !!this._globalOptions.whitelistUrls.test &&
562 !this._globalOptions.whitelistUrls.test(fileurl)
563 ) {
564 return;
565 }
622 566
623 return this;567 if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
624 },568 options = objectMerge(
569 {
570 // fingerprint on msg, not stack trace (legacy behavior, could be
571 // revisited)
572 fingerprint: msg,
573 // since we know this is a synthetic trace, the top N-most frames
574 // MUST be from Raven.js, so mark them as in_app later by setting
575 // trimHeadFrames
576 trimHeadFrames: (options.trimHeadFrames || 0) + 1
577 },
578 options
579 );
580
581 var frames = this._prepareFrames(stack, options);
582 data.stacktrace = {
583 // Sentry expects frames oldest to newest
584 frames: frames.reverse()
585 };
586 }
625 587
626 captureBreadcrumb: function(obj) {588 // Fire away!
627 var crumb = objectMerge(589 this._send(data);
628 {
629 timestamp: now() / 1000
630 },
631 obj
632 );
633 590
634 if (isFunction(this._globalOptions.breadcrumbCallback)) {591 return this;
635 var result = this._globalOptions.breadcrumbCallback(crumb);592 },
636 593
637 if (isObject(result) && !isEmptyObject(result)) {594 captureBreadcrumb: function(obj) {
638 crumb = result;595 var crumb = objectMerge(
639 } else if (result === false) {596 {
640 return this;597 timestamp: now() / 1000
641 }598 },
642 }599 obj
600 );
643 601
644 this._breadcrumbs.push(crumb);602 if (isFunction(this._globalOptions.breadcrumbCallback)) {
645 if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {603 var result = this._globalOptions.breadcrumbCallback(crumb);
646 this._breadcrumbs.shift();604
647 }605 if (isObject(result) && !isEmptyObject(result)) {
648 return this;606 crumb = result;
649 },607 } else if (result === false) {
608 return this;
609 }
610 }
650 611
651 addPlugin: function(plugin /*arg1, arg2, ... argN*/) {612 this._breadcrumbs.push(crumb);
652 var pluginArgs = [].slice.call(arguments, 1);613 if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
614 this._breadcrumbs.shift();
615 }
616 return this;
617 },
653 618
654 this._plugins.push([plugin, pluginArgs]);619 addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
655 if (this._isRavenInstalled) {620 var pluginArgs = [].slice.call(arguments, 1);
656 this._drainPlugins();
657 }
658 621
659 return this;622 this._plugins.push([plugin, pluginArgs]);
660 },623 if (this._isRavenInstalled) {
624 this._drainPlugins();
625 }
626
627 return this;
628 },
661 629
662 /*630 /*
663 * Set/clear a user to be sent along with the payload.631 * Set/clear a user to be sent along with the payload.
664 *632 *
665 * @param {object} user An object representing user data [optional]633 * @param {object} user An object representing user data [optional]
666 * @return {Raven}634 * @return {Raven}
667 */635 */
668 setUserContext: function(user) {636 setUserContext: function(user) {
669 // Intentionally do not merge here since that's an unexpected behavior.637 // Intentionally do not merge here since that's an unexpected behavior.
670 this._globalContext.user = user;638 this._globalContext.user = user;
671 639
672 return this;640 return this;
673 },641 },
674 642
675 /*643 /*
676 * Merge extra attributes to be sent along with the payload.644 * Merge extra attributes to be sent along with the payload.
677 *645 *
678 * @param {object} extra An object representing extra data [optional]646 * @param {object} extra An object representing extra data [optional]
679 * @return {Raven}647 * @return {Raven}
680 */648 */
681 setExtraContext: function(extra) {649 setExtraContext: function(extra) {
682 this._mergeContext('extra', extra);650 this._mergeContext('extra', extra);
683 651
684 return this;652 return this;
685 },653 },
686 654
687 /*655 /*
688 * Merge tags to be sent along with the payload.656 * Merge tags to be sent along with the payload.
689 *657 *
690 * @param {object} tags An object representing tags [optional]658 * @param {object} tags An object representing tags [optional]
691 * @return {Raven}659 * @return {Raven}
692 */660 */
693 setTagsContext: function(tags) {661 setTagsContext: function(tags) {
694 this._mergeContext('tags', tags);662 this._mergeContext('tags', tags);
695 663
696 return this;664 return this;
697 },665 },
698 666
699 /*667 /*
700 * Clear all of the context.668 * Clear all of the context.
701 *669 *
702 * @return {Raven}670 * @return {Raven}
703 */671 */
704 clearContext: function() {672 clearContext: function() {
705 this._globalContext = {};673 this._globalContext = {};
706 674
707 return this;675 return this;
708 },676 },
709 677
710 /*678 /*
711 * Get a copy of the current context. This cannot be mutated.679 * Get a copy of the current context. This cannot be mutated.
712 *680 *
713 * @return {object} copy of context681 * @return {object} copy of context
714 */682 */
715 getContext: function() {683 getContext: function() {
716 // lol javascript684 // lol javascript
717 return JSON.parse(stringify(this._globalContext));685 return JSON.parse(stringify(this._globalContext));
718 },686 },
719 687
720 /*688 /*
721 * Set environment of application689 * Set environment of application
722 *690 *
723 * @param {string} environment Typically something like 'production'.691 * @param {string} environment Typically something like 'production'.
724 * @return {Raven}692 * @return {Raven}
725 */693 */
726 setEnvironment: function(environment) {694 setEnvironment: function(environment) {
727 this._globalOptions.environment = environment;695 this._globalOptions.environment = environment;
728 696
729 return this;697 return this;
730 },698 },
731 699
732 /*700 /*
733 * Set release version of application701 * Set release version of application
734 *702 *
735 * @param {string} release Typically something like a git SHA to identify version703 * @param {string} release Typically something like a git SHA to identify version
736 * @return {Raven}704 * @return {Raven}
737 */705 */
738 setRelease: function(release) {706 setRelease: function(release) {
739 this._globalOptions.release = release;707 this._globalOptions.release = release;
740 708
741 return this;709 return this;
742 },710 },
743 711
744 /*712 /*
745 * Set the dataCallback option713 * Set the dataCallback option
746 *714 *
747 * @param {function} callback The callback to run which allows the715 * @param {function} callback The callback to run which allows the
748 * data blob to be mutated before sending716 * data blob to be mutated before sending
749 * @return {Raven}717 * @return {Raven}
750 */718 */
751 setDataCallback: function(callback) {719 setDataCallback: function(callback) {
752 var original = this._globalOptions.dataCallback;720 var original = this._globalOptions.dataCallback;
753 this._globalOptions.dataCallback = keepOriginalCallback(721 this._globalOptions.dataCallback = keepOriginalCallback(original, callback);
754 original,722 return this;
755 callback723 },
756 );724
757 return this;725 /*
758 },
759
760 /*
761 * Set the breadcrumbCallback option726 * Set the breadcrumbCallback option
762 *727 *
763 * @param {function} callback The callback to run which allows filtering728 * @param {function} callback The callback to run which allows filtering
764 * or mutating breadcrumbs729 * or mutating breadcrumbs
765 * @return {Raven}730 * @return {Raven}
766 */731 */
767 setBreadcrumbCallback: function(callback) {732 setBreadcrumbCallback: function(callback) {
768 var original = this._globalOptions.breadcrumbCallback;733 var original = this._globalOptions.breadcrumbCallback;
769 this._globalOptions.breadcrumbCallback = keepOriginalCallback(734 this._globalOptions.breadcrumbCallback = keepOriginalCallback(original, callback);
770 original,735 return this;
771 callback736 },
772 );737
773 return this;738 /*
774 },
775
776 /*
777 * Set the shouldSendCallback option739 * Set the shouldSendCallback option
778 *740 *
779 * @param {function} callback The callback to run which allows741 * @param {function} callback The callback to run which allows
780 * introspecting the blob before sending742 * introspecting the blob before sending
781 * @return {Raven}743 * @return {Raven}
782 */744 */
783 setShouldSendCallback: function(callback) {745 setShouldSendCallback: function(callback) {
784 var original = this._globalOptions.shouldSendCallback;746 var original = this._globalOptions.shouldSendCallback;
785 this._globalOptions.shouldSendCallback = keepOriginalCallback(747 this._globalOptions.shouldSendCallback = keepOriginalCallback(original, callback);
786 original,748 return this;
787 callback749 },
788 );750
789 return this;751 /**
790 },
791
792 /**
793 * Override the default HTTP transport mechanism that transmits data752 * Override the default HTTP transport mechanism that transmits data
794 * to the Sentry server.753 * to the Sentry server.
795 *754 *
@@ -798,438 +757,443 @@ · 2 unmodified lines
798 *757 *
799 * @return {Raven}758 * @return {Raven}
800 */759 */
801 setTransport: function(transport) {760 setTransport: function(transport) {
802 this._globalOptions.transport = transport;761 this._globalOptions.transport = transport;
803 762
804 return this;763 return this;
805 },764 },
806 765
807 /*766 /*
808 * Get the latest raw exception that was captured by Raven.767 * Get the latest raw exception that was captured by Raven.
809 *768 *
810 * @return {error}769 * @return {error}
811 */770 */
812 lastException: function() {771 lastException: function() {
813 return this._lastCapturedException;772 return this._lastCapturedException;
814 },773 },
815 774
816 /*775 /*
817 * Get the last event id776 * Get the last event id
818 *777 *
819 * @return {string}778 * @return {string}
820 */779 */
821 lastEventId: function() {780 lastEventId: function() {
822 return this._lastEventId;781 return this._lastEventId;
823 },782 },
824 783
825 /*784 /*
826 * Determine if Raven is setup and ready to go.785 * Determine if Raven is setup and ready to go.
827 *786 *
828 * @return {boolean}787 * @return {boolean}
829 */788 */
830 isSetup: function() {789 isSetup: function() {
831 if (!this._hasJSON) return false; // needs JSON support790 if (!this._hasJSON) return false; // needs JSON support
832 if (!this._globalServer) {791 if (!this._globalServer) {
833 if (!this.ravenNotConfiguredError) {792 if (!this.ravenNotConfiguredError) {
834 this.ravenNotConfiguredError = true;793 this.ravenNotConfiguredError = true;
835 this._logDebug('error', 'Error: Raven has not been configured.');794 this._logDebug('error', 'Error: Raven has not been configured.');
836 }795 }
837 return false;796 return false;
838 }797 }
839 return true;798 return true;
840 },799 },
841 800
842 afterLoad: function() {801 afterLoad: function() {
843 // TODO: remove window dependence?802 // TODO: remove window dependence?
844 803
845 // Attempt to initialize Raven on load804 // Attempt to initialize Raven on load
846 var RavenConfig = _window.RavenConfig;805 var RavenConfig = _window.RavenConfig;
847 if (RavenConfig) {806 if (RavenConfig) {
848 this.config(RavenConfig.dsn, RavenConfig.config).install();807 this.config(RavenConfig.dsn, RavenConfig.config).install();
849 }808 }
850 },809 },
851 810
852 showReportDialog: function(options) {811 showReportDialog: function(options) {
853 if (812 if (
854 !_document // doesn't work without a document (React native)813 !_document // doesn't work without a document (React native)
855 )814 )
856 return;815 return;
857 816
858 options = options || {};817 options = options || {};
859 818
860 var lastEventId = options.eventId || this.lastEventId();819 var lastEventId = options.eventId || this.lastEventId();
861 if (!lastEventId) {820 if (!lastEventId) {
862 throw new RavenConfigError('Missing eventId');821 throw new RavenConfigError('Missing eventId');
863 }822 }
864 823
865 var dsn = options.dsn || this._dsn;824 var dsn = options.dsn || this._dsn;
866 if (!dsn) {825 if (!dsn) {
867 throw new RavenConfigError('Missing DSN');826 throw new RavenConfigError('Missing DSN');
868 }827 }
869 828
870 var encode = encodeURIComponent;829 var encode = encodeURIComponent;
871 var qs = '';830 var qs = '';
872 qs += '?eventId=' + encode(lastEventId);831 qs += '?eventId=' + encode(lastEventId);
873 qs += '&dsn=' + encode(dsn);832 qs += '&dsn=' + encode(dsn);
874 833
875 var user = options.user || this._globalContext.user;834 var user = options.user || this._globalContext.user;
876 if (user) {835 if (user) {
877 if (user.name) qs += '&name=' + encode(user.name);836 if (user.name) qs += '&name=' + encode(user.name);
878 if (user.email) qs += '&email=' + encode(user.email);837 if (user.email) qs += '&email=' + encode(user.email);
879 }838 }
880 839
881 var globalServer = this._getGlobalServer(this._parseDSN(dsn));840 var globalServer = this._getGlobalServer(this._parseDSN(dsn));
882
883 var script = _document.createElement('script');
884 script.async = true;
885 script.src = globalServer + '/api/embed/error-page/' + qs;
886 (_document.head || _document.body).appendChild(script);
887 },
888
889 /**** Private functions ****/
890 _ignoreNextOnError: function() {
891 var self = this;
892 this._ignoreOnError += 1;
893 setTimeout(function() {
894 // onerror should trigger before setTimeout
895 self._ignoreOnError -= 1;
896 });
897 },
898 841
899 _triggerEvent: function(eventType, options) {842 var script = _document.createElement('script');
900 // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it843 script.async = true;
901 var evt, key;844 script.src = globalServer + '/api/embed/error-page/' + qs;
845 (_document.head || _document.body).appendChild(script);
846 },
902 847
903 if (!this._hasDocument) return;848 /**** Private functions ****/
849 _ignoreNextOnError: function() {
850 var self = this;
851 this._ignoreOnError += 1;
852 setTimeout(function() {
853 // onerror should trigger before setTimeout
854 self._ignoreOnError -= 1;
855 });
856 },
904 857
905 options = options || {};858 _triggerEvent: function(eventType, options) {
859 // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
860 var evt, key;
906 861
907 eventType =862 if (!this._hasDocument) return;
908 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1);
909 863
910 if (_document.createEvent) {864 options = options || {};
911 evt = _document.createEvent('HTMLEvents');
912 evt.initEvent(eventType, true, true);
913 } else {
914 evt = _document.createEventObject();
915 evt.eventType = eventType;
916 }
917 865
918 for (key in options)866 eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1);
919 if (hasKey(options, key)) {
920 evt[key] = options[key];
921 }
922 867
923 if (_document.createEvent) {868 if (_document.createEvent) {
924 // IE9 if standards869 evt = _document.createEvent('HTMLEvents');
925 _document.dispatchEvent(evt);870 evt.initEvent(eventType, true, true);
926 } else {871 } else {
927 // IE8 regardless of Quirks or Standards872 evt = _document.createEventObject();
928 // IE9 if quirks873 evt.eventType = eventType;
929 try {874 }
930 _document.fireEvent('on' + evt.eventType.toLowerCase(), evt);875
931 } catch (e) {876 for (key in options)
932 // Do nothing877 if (hasKey(options, key)) {
933 }878 evt[key] = options[key];
934 }879 }
935 },880
881 if (_document.createEvent) {
882 // IE9 if standards
883 _document.dispatchEvent(evt);
884 } else {
885 // IE8 regardless of Quirks or Standards
886 // IE9 if quirks
887 try {
888 _document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
889 } catch (e) {
890 // Do nothing
891 }
892 }
893 },
936 894
937 /**895 /**
938 * Wraps addEventListener to capture UI breadcrumbs896 * Wraps addEventListener to capture UI breadcrumbs
939 * @param evtName the event name (e.g. "click")897 * @param evtName the event name (e.g. "click")
940 * @returns {Function}898 * @returns {Function}
941 * @private899 * @private
942 */900 */
943 _breadcrumbEventHandler: function(evtName) {901 _breadcrumbEventHandler: function(evtName) {
944 var self = this;902 var self = this;
945 return function(evt) {903 return function(evt) {
946 // reset keypress timeout; e.g. triggering a 'click' after904 // reset keypress timeout; e.g. triggering a 'click' after
947 // a 'keypress' will reset the keypress debounce so that a new905 // a 'keypress' will reset the keypress debounce so that a new
948 // set of keypresses can be recorded906 // set of keypresses can be recorded
949 self._keypressTimeout = null;907 self._keypressTimeout = null;
950 908
951 // It's possible this handler might trigger multiple times for the same909 // It's possible this handler might trigger multiple times for the same
952 // event (e.g. event propagation through node ancestors). Ignore if we've910 // event (e.g. event propagation through node ancestors). Ignore if we've
953 // already captured the event.911 // already captured the event.
954 if (self._lastCapturedEvent === evt) return;912 if (self._lastCapturedEvent === evt) return;
955 913
956 self._lastCapturedEvent = evt;914 self._lastCapturedEvent = evt;
957 915
958 // try/catch both:916 // try/catch both:
959 // - accessing evt.target (see getsentry/raven-js#838, #768)917 // - accessing evt.target (see getsentry/raven-js#838, #768)
960 // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly918 // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly
961 // can throw an exception in some circumstances.919 // can throw an exception in some circumstances.
962 var target;920 var target;
963 try {921 try {
964 target = htmlTreeAsString(evt.target);922 target = htmlTreeAsString(evt.target);
965 } catch (e) {923 } catch (e) {
966 target = '<unknown>';924 target = '<unknown>';
967 }925 }
968 926
969 self.captureBreadcrumb({927 self.captureBreadcrumb({
970 category: 'ui.' + evtName, // e.g. ui.click, ui.input928 category: 'ui.' + evtName, // e.g. ui.click, ui.input
971 message: target929 message: target
972 });930 });
973 };931 };
974 },932 },
975 933
976 /**934 /**
977 * Wraps addEventListener to capture keypress UI events935 * Wraps addEventListener to capture keypress UI events
978 * @returns {Function}936 * @returns {Function}
979 * @private937 * @private
980 */938 */
981 _keypressEventHandler: function() {939 _keypressEventHandler: function() {
982 var self = this,940 var self = this,
983 debounceDuration = 1000; // milliseconds941 debounceDuration = 1000; // milliseconds
984 942
985 // TODO: if somehow user switches keypress target before943 // TODO: if somehow user switches keypress target before
986 // debounce timeout is triggered, we will only capture944 // debounce timeout is triggered, we will only capture
987 // a single breadcrumb from the FIRST target (acceptable?)945 // a single breadcrumb from the FIRST target (acceptable?)
988 return function(evt) {946 return function(evt) {
989 var target;947 var target;
990 try {948 try {
991 target = evt.target;949 target = evt.target;
992 } catch (e) {950 } catch (e) {
993 // just accessing event properties can throw an exception in some rare circumstances951 // just accessing event properties can throw an exception in some rare circumstances
994 // see: https://github.com/getsentry/raven-js/issues/838952 // see: https://github.com/getsentry/raven-js/issues/838
995 return;953 return;
996 }954 }
997 var tagName = target && target.tagName;955 var tagName = target && target.tagName;
998 956
999 // only consider keypress events on actual input elements957 // only consider keypress events on actual input elements
1000 // this will disregard keypresses targeting body (e.g. tabbing958 // this will disregard keypresses targeting body (e.g. tabbing
1001 // through elements, hotkeys, etc)959 // through elements, hotkeys, etc)
1002 if (960 if (
1003 !tagName ||961 !tagName ||
1004 (tagName !== 'INPUT' &&962 (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !target.isContentEditable)
1005 tagName !== 'TEXTAREA' &&963 )
1006 !target.isContentEditable)964 return;
1007 )965
1008 return;966 // record first keypress in a series, but ignore subsequent
1009 967 // keypresses until debounce clears
1010 // record first keypress in a series, but ignore subsequent968 var timeout = self._keypressTimeout;
1011 // keypresses until debounce clears969 if (!timeout) {
1012 var timeout = self._keypressTimeout;970 self._breadcrumbEventHandler('input')(evt);
1013 if (!timeout) {971 }
1014 self._breadcrumbEventHandler('input')(evt);972 clearTimeout(timeout);
1015 }973 self._keypressTimeout = setTimeout(function() {
1016 clearTimeout(timeout);974 self._keypressTimeout = null;
1017 self._keypressTimeout = setTimeout(function() {975 }, debounceDuration);
1018 self._keypressTimeout = null;976 };
1019 }, debounceDuration);977 },
1020 };978
1021 },979 /**
1022
1023 /**
1024 * Captures a breadcrumb of type "navigation", normalizing input URLs980 * Captures a breadcrumb of type "navigation", normalizing input URLs
1025 * @param to the originating URL981 * @param to the originating URL
1026 * @param from the target URL982 * @param from the target URL
1027 * @private983 * @private
1028 */984 */
1029 _captureUrlChange: function(from, to) {985 _captureUrlChange: function(from, to) {
1030 var parsedLoc = parseUrl(this._location.href);986 var parsedLoc = parseUrl(this._location.href);
1031 var parsedTo = parseUrl(to);987 var parsedTo = parseUrl(to);
1032 var parsedFrom = parseUrl(from);988 var parsedFrom = parseUrl(from);
1033 989
1034 // because onpopstate only tells you the "new" (to) value of location.href, and990 // because onpopstate only tells you the "new" (to) value of location.href, and
1035 // not the previous (from) value, we need to track the value of the current URL991 // not the previous (from) value, we need to track the value of the current URL
1036 // state ourselves992 // state ourselves
1037 this._lastHref = to;993 this._lastHref = to;
1038 994
1039 // Use only the path component of the URL if the URL matches the current995 // Use only the path component of the URL if the URL matches the current
1040 // document (almost all the time when using pushState)996 // document (almost all the time when using pushState)
1041 if (997 if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host)
1042 parsedLoc.protocol === parsedTo.protocol &&998 to = parsedTo.relative;
1043 parsedLoc.host === parsedTo.host999 if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host)
1044 )1000 from = parsedFrom.relative;
1045 to = parsedTo.relative;1001
1046 if (1002 this.captureBreadcrumb({
1047 parsedLoc.protocol === parsedFrom.protocol &&1003 category: 'navigation',
1048 parsedLoc.host === parsedFrom.host1004 data: {
1049 )1005 to: to,
1050 from = parsedFrom.relative;1006 from: from
1051 1007 }
1052 this.captureBreadcrumb({1008 });
1053 category: 'navigation',1009 },
1054 data: {1010
1055 to: to,1011 _patchFunctionToString: function() {
1056 from: from1012 var self = this;
1057 }1013 self._originalFunctionToString = Function.prototype.toString;
1058 });1014 // eslint-disable-next-line no-extend-native
1059 },1015 Function.prototype.toString = function() {
1016 if (typeof this === 'function' && this.__raven__) {
1017 return self._originalFunctionToString.apply(this.__orig__, arguments);
1018 }
1019 return self._originalFunctionToString.apply(this, arguments);
1020 };
1021 },
1022
1023 _unpatchFunctionToString: function() {
1024 if (this._originalFunctionToString) {
1025 // eslint-disable-next-line no-extend-native
1026 Function.prototype.toString = this._originalFunctionToString;
1027 }
1028 },
1060 1029
1061 /**1030 /**
1062 * Wrap timer functions and event targets to catch errors and provide1031 * Wrap timer functions and event targets to catch errors and provide
1063 * better metadata.1032 * better metadata.
1064 */1033 */
1065 _instrumentTryCatch: function() {1034 _instrumentTryCatch: function() {
1066 var self = this;1035 var self = this;
1067 1036
1068 var wrappedBuiltIns = self._wrappedBuiltIns;1037 var wrappedBuiltIns = self._wrappedBuiltIns;
1069 1038
1070 function wrapTimeFn(orig) {1039 function wrapTimeFn(orig) {
1071 return function(fn, t) {1040 return function(fn, t) {
1072 // preserve arity1041 // preserve arity
1073 // Make a copy of the arguments to prevent deoptimization1042 // Make a copy of the arguments to prevent deoptimization
1074 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments1043 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1075 var args = new Array(arguments.length);1044 var args = new Array(arguments.length);
1076 for (var i = 0; i < args.length; ++i) {1045 for (var i = 0; i < args.length; ++i) {
1077 args[i] = arguments[i];1046 args[i] = arguments[i];
1078 }1047 }
1079 var originalCallback = args[0];1048 var originalCallback = args[0];
1080 if (isFunction(originalCallback)) {1049 if (isFunction(originalCallback)) {
1081 args[0] = self.wrap(originalCallback);1050 args[0] = self.wrap(originalCallback);
1082 }1051 }
1083 1052
1084 // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it1053 // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
1085 // also supports only two arguments and doesn't care what this is, so we1054 // also supports only two arguments and doesn't care what this is, so we
1086 // can just call the original function directly.1055 // can just call the original function directly.
1087 if (orig.apply) {1056 if (orig.apply) {
1088 return orig.apply(this, args);1057 return orig.apply(this, args);
1089 } else {1058 } else {
1090 return orig(args[0], args[1]);1059 return orig(args[0], args[1]);
1091 }1060 }
1092 };1061 };
1093 }1062 }
1063
1064 var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1065
1066 function wrapEventTarget(global) {
1067 var proto = _window[global] && _window[global].prototype;
1068 if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) {
1069 fill(
1070 proto,
1071 'addEventListener',
1072 function(orig) {
1073 return function(evtName, fn, capture, secure) {
1074 // preserve arity
1075 try {
1076 if (fn && fn.handleEvent) {
1077 fn.handleEvent = self.wrap(fn.handleEvent);
1078 }
1079 } catch (err) {
1080 // can sometimes get 'Permission denied to access property "handle Event'
1081 }
1094 1082
1095 var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;1083 // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`
1096 1084 // so that we don't have more than one wrapper function
1097 function wrapEventTarget(global) {1085 var before, clickHandler, keypressHandler;
1098 var proto = _window[global] && _window[global].prototype;1086
1099 if (1087 if (
1100 proto &&1088 autoBreadcrumbs &&
1101 proto.hasOwnProperty &&1089 autoBreadcrumbs.dom &&
1102 proto.hasOwnProperty('addEventListener')1090 (global === 'EventTarget' || global === 'Node')
1103 ) {1091 ) {
1104 fill(1092 // NOTE: generating multiple handlers per addEventListener invocation, should
1105 proto,1093 // revisit and verify we can just use one (almost certainly)
1106 'addEventListener',1094 clickHandler = self._breadcrumbEventHandler('click');
1107 function(orig) {1095 keypressHandler = self._keypressEventHandler();
1108 return function(evtName, fn, capture, secure) {1096 before = function(evt) {
1109 // preserve arity1097 // need to intercept every DOM event in `before` argument, in case that
1110 try {1098 // same wrapped method is re-used for different events (e.g. mousemove THEN click)
1111 if (fn && fn.handleEvent) {1099 // see #724
1112 fn.handleEvent = self.wrap(fn.handleEvent);1100 if (!evt) return;
1113 }1101
1114 } catch (err) {1102 var eventType;
1115 // can sometimes get 'Permission denied to access property "handle Event'1103 try {
1116 }1104 eventType = evt.type;
1117 1105 } catch (e) {
1118 // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`1106 // just accessing event properties can throw an exception in some rare circumstances
1119 // so that we don't have more than one wrapper function1107 // see: https://github.com/getsentry/raven-js/issues/838
1120 var before, clickHandler, keypressHandler;1108 return;
1121
1122 if (
1123 autoBreadcrumbs &&
1124 autoBreadcrumbs.dom &&
1125 (global === 'EventTarget' || global === 'Node')
1126 ) {
1127 // NOTE: generating multiple handlers per addEventListener invocation, should
1128 // revisit and verify we can just use one (almost certainly)
1129 clickHandler = self._breadcrumbEventHandler('click');
1130 keypressHandler = self._keypressEventHandler();
1131 before = function(evt) {
1132 // need to intercept every DOM event in `before` argument, in case that
1133 // same wrapped method is re-used for different events (e.g. mousemove THEN click)
1134 // see #724
1135 if (!evt) return;
1136
1137 var eventType;
1138 try {
1139 eventType = evt.type;
1140 } catch (e) {
1141 // just accessing event properties can throw an exception in some rare circumstances
1142 // see: https://github.com/getsentry/raven-js/issues/838
1143 return;
1144 }
1145 if (eventType === 'click') return clickHandler(evt);
1146 else if (eventType === 'keypress')
1147 return keypressHandler(evt);
1148 };
1149 }
1150 return orig.call(
1151 this,
1152 evtName,
1153 self.wrap(fn, undefined, before),
1154 capture,
1155 secure
1156 );
1157 };
1158 },
1159 wrappedBuiltIns
1160 );
1161 fill(
1162 proto,
1163 'removeEventListener',
1164 function(orig) {
1165 return function(evt, fn, capture, secure) {
1166 try {
1167 fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn);
1168 } catch (e) {
1169 // ignore, accessing __raven_wrapper__ will throw in some Selenium environments
1170 }
1171 return orig.call(this, evt, fn, capture, secure);
1172 };
1173 },
1174 wrappedBuiltIns
1175 );
1176 }1109 }
1177 }1110 if (eventType === 'click') return clickHandler(evt);
1111 else if (eventType === 'keypress') return keypressHandler(evt);
1112 };
1113 }
1114 return orig.call(
1115 this,
1116 evtName,
1117 self.wrap(fn, undefined, before),
1118 capture,
1119 secure
1120 );
1121 };
1122 },
1123 wrappedBuiltIns
1124 );
1125 fill(
1126 proto,
1127 'removeEventListener',
1128 function(orig) {
1129 return function(evt, fn, capture, secure) {
1130 try {
1131 fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn);
1132 } catch (e) {
1133 // ignore, accessing __raven_wrapper__ will throw in some Selenium environments
1134 }
1135 return orig.call(this, evt, fn, capture, secure);
1136 };
1137 },
1138 wrappedBuiltIns
1139 );
1140 }
1141 }
1178 1142
1179 fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns);1143 fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns);
1180 fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns);1144 fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns);
1181 if (_window.requestAnimationFrame) {1145 if (_window.requestAnimationFrame) {
1182 fill(1146 fill(
1183 _window,1147 _window,
1184 'requestAnimationFrame',1148 'requestAnimationFrame',
1185 function(orig) {1149 function(orig) {
1186 return function(cb) {1150 return function(cb) {
1187 return orig(self.wrap(cb));1151 return orig(self.wrap(cb));
1188 };1152 };
1189 },1153 },
1190 wrappedBuiltIns1154 wrappedBuiltIns
1191 );1155 );
1192 }1156 }
1193 1157
1194 // event targets borrowed from bugsnag-js:1158 // event targets borrowed from bugsnag-js:
1195 // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L6661159 // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
1196 var eventTargets = [1160 var eventTargets = [
1197 'EventTarget',1161 'EventTarget',
1198 'Window',1162 'Window',
1199 'Node',1163 'Node',
1200 'ApplicationCache',1164 'ApplicationCache',
1201 'AudioTrackList',1165 'AudioTrackList',
1202 'ChannelMergerNode',1166 'ChannelMergerNode',
1203 'CryptoOperation',1167 'CryptoOperation',
1204 'EventSource',1168 'EventSource',
1205 'FileReader',1169 'FileReader',
1206 'HTMLUnknownElement',1170 'HTMLUnknownElement',
1207 'IDBDatabase',1171 'IDBDatabase',
1208 'IDBRequest',1172 'IDBRequest',
1209 'IDBTransaction',1173 'IDBTransaction',
1210 'KeyOperation',1174 'KeyOperation',
1211 'MediaController',1175 'MediaController',
1212 'MessagePort',1176 'MessagePort',
1213 'ModalWindow',1177 'ModalWindow',
1214 'Notification',1178 'Notification',
1215 'SVGElementInstance',1179 'SVGElementInstance',
1216 'Screen',1180 'Screen',
1217 'TextTrack',1181 'TextTrack',
1218 'TextTrackCue',1182 'TextTrackCue',
1219 'TextTrackList',1183 'TextTrackList',
1220 'WebSocket',1184 'WebSocket',
1221 'WebSocketWorker',1185 'WebSocketWorker',
1222 'Worker',1186 'Worker',
1223 'XMLHttpRequest',1187 'XMLHttpRequest',
1224 'XMLHttpRequestEventTarget',1188 'XMLHttpRequestEventTarget',
1225 'XMLHttpRequestUpload'1189 'XMLHttpRequestUpload'
1226 ];1190 ];
1227 for (var i = 0; i < eventTargets.length; i++) {1191 for (var i = 0; i < eventTargets.length; i++) {
1228 wrapEventTarget(eventTargets[i]);1192 wrapEventTarget(eventTargets[i]);
1229 }1193 }
1230 },1194 },
1231 1195
1232 /**1196 /**
1233 * Instrument browser built-ins w/ breadcrumb capturing1197 * Instrument browser built-ins w/ breadcrumb capturing
1234 * - XMLHttpRequests1198 * - XMLHttpRequests
1235 * - DOM interactions (click/typing)1199 * - DOM interactions (click/typing)
@@ -1238,546 +1202,515 @@ · 2 unmodified lines
1238 *1202 *
1239 * Can be disabled or individually configured via the `autoBreadcrumbs` config option1203 * Can be disabled or individually configured via the `autoBreadcrumbs` config option
1240 */1204 */
1241 _instrumentBreadcrumbs: function() {1205 _instrumentBreadcrumbs: function() {
1242 var self = this;1206 var self = this;
1243 var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;1207 var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
1244 1208
1245 var wrappedBuiltIns = self._wrappedBuiltIns;1209 var wrappedBuiltIns = self._wrappedBuiltIns;
1246 1210
1247 function wrapProp(prop, xhr) {1211 function wrapProp(prop, xhr) {
1248 if (prop in xhr && isFunction(xhr[prop])) {1212 if (prop in xhr && isFunction(xhr[prop])) {
1249 fill(xhr, prop, function(orig) {1213 fill(xhr, prop, function(orig) {
1250 return self.wrap(orig);1214 return self.wrap(orig);
1251 }); // intentionally don't track filled methods on XHR instances1215 }); // intentionally don't track filled methods on XHR instances
1252 }1216 }
1253 }1217 }
1254 1218
1255 if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) {1219 if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) {
1256 var xhrproto = XMLHttpRequest.prototype;1220 var xhrproto = XMLHttpRequest.prototype;
1257 fill(1221 fill(
1258 xhrproto,1222 xhrproto,
1259 'open',1223 'open',
1260 function(origOpen) {1224 function(origOpen) {
1261 return function(method, url) {1225 return function(method, url) {
1262 // preserve arity1226 // preserve arity
1263 1227
1264 // if Sentry key appears in URL, don't capture1228 // if Sentry key appears in URL, don't capture
1265 if (isString(url) && url.indexOf(self._globalKey) === -1) {1229 if (isString(url) && url.indexOf(self._globalKey) === -1) {
1266 this.__raven_xhr = {1230 this.__raven_xhr = {
1267 method: method,1231 method: method,
1268 url: url,1232 url: url,
1269 status_code: null1233 status_code: null
1270 };1234 };
1271 }1235 }
1272
1273 return origOpen.apply(this, arguments);
1274 };
1275 },
1276 wrappedBuiltIns
1277 );
1278
1279 fill(
1280 xhrproto,
1281 'send',
1282 function(origSend) {
1283 return function(data) {
1284 // preserve arity
1285 var xhr = this;
1286
1287 function onreadystatechangeHandler() {
1288 if (xhr.__raven_xhr && xhr.readyState === 4) {
1289 try {
1290 // touching statusCode in some platforms throws
1291 // an exception
1292 xhr.__raven_xhr.status_code = xhr.status;
1293 } catch (e) {
1294 /* do nothing */
1295 }
1296
1297 self.captureBreadcrumb({
1298 type: 'http',
1299 category: 'xhr',
1300 data: xhr.__raven_xhr
1301 });
1302 }
1303 }
1304
1305 var props = ['onload', 'onerror', 'onprogress'];
1306 for (var j = 0; j < props.length; j++) {
1307 wrapProp(props[j], xhr);
1308 }
1309
1310 if (
1311 'onreadystatechange' in xhr &&
1312 isFunction(xhr.onreadystatechange)
1313 ) {
1314 fill(
1315 xhr,
1316 'onreadystatechange',
1317 function(orig) {
1318 return self.wrap(
1319 orig,
1320 undefined,
1321 onreadystatechangeHandler
1322 );
1323 } /* intentionally don't track this instrumentation */
1324 );
1325 } else {
1326 // if onreadystatechange wasn't actually set by the page on this xhr, we
1327 // are free to set our own and capture the breadcrumb
1328 xhr.onreadystatechange = onreadystatechangeHandler;
1329 }
1330
1331 return origSend.apply(this, arguments);
1332 };
1333 },
1334 wrappedBuiltIns
1335 );
1336 }
1337 1236
1338 if (autoBreadcrumbs.xhr && 'fetch' in _window) {1237 return origOpen.apply(this, arguments);
1339 fill(1238 };
1340 _window,1239 },
1341 'fetch',1240 wrappedBuiltIns
1342 function(origFetch) {1241 );
1343 return function(fn, t) {1242
1344 // preserve arity1243 fill(
1345 // Make a copy of the arguments to prevent deoptimization1244 xhrproto,
1346 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments1245 'send',
1347 var args = new Array(arguments.length);1246 function(origSend) {
1348 for (var i = 0; i < args.length; ++i) {1247 return function(data) {
1349 args[i] = arguments[i];1248 // preserve arity
1350 }1249 var xhr = this;
1351 1250
1352 var fetchInput = args[0];1251 function onreadystatechangeHandler() {
1353 var method = 'GET';1252 if (xhr.__raven_xhr && xhr.readyState === 4) {
1354 var url;1253 try {
1355 1254 // touching statusCode in some platforms throws
1356 if (typeof fetchInput === 'string') {1255 // an exception
1357 url = fetchInput;1256 xhr.__raven_xhr.status_code = xhr.status;
1358 } else {1257 } catch (e) {
1359 url = fetchInput.url;1258 /* do nothing */
1360 if (fetchInput.method) {
1361 method = fetchInput.method;
1362 }
1363 }
1364
1365 if (args[1] && args[1].method) {
1366 method = args[1].method;
1367 }
1368
1369 var fetchData = {
1370 method: method,
1371 url: url,
1372 status_code: null
1373 };
1374
1375 self.captureBreadcrumb({
1376 type: 'http',
1377 category: 'fetch',
1378 data: fetchData
1379 });
1380
1381 return origFetch.apply(this, args).then(function(response) {
1382 fetchData.status_code = response.status;
1383
1384 return response;
1385 });
1386 };
1387 },
1388 wrappedBuiltIns
1389 );
1390 }1259 }
1391 1260
1392 // Capture breadcrumbs from any click that is unhandled / bubbled up all the way1261 self.captureBreadcrumb({
1393 // to the document. Do this before we instrument addEventListener.1262 type: 'http',
1394 if (autoBreadcrumbs.dom && this._hasDocument) {1263 category: 'xhr',
1395 if (_document.addEventListener) {1264 data: xhr.__raven_xhr
1396 _document.addEventListener(1265 });
1397 'click',1266 }
1398 self._breadcrumbEventHandler('click'),1267 }
1399 false
1400 );
1401 _document.addEventListener(
1402 'keypress',
1403 self._keypressEventHandler(),
1404 false
1405 );
1406 } else {
1407 // IE8 Compatibility
1408 _document.attachEvent(
1409 'onclick',
1410 self._breadcrumbEventHandler('click')
1411 );
1412 _document.attachEvent('onkeypress', self._keypressEventHandler());
1413 }
1414 }
1415 1268
1416 // record navigation (URL) changes1269 var props = ['onload', 'onerror', 'onprogress'];
1417 // NOTE: in Chrome App environment, touching history.pushState, *even inside1270 for (var j = 0; j < props.length; j++) {
1418 // a try/catch block*, will cause Chrome to output an error to console.error1271 wrapProp(props[j], xhr);
1419 // borrowed from: https://github.com/angular/angular.js/pull/13945/files1272 }
1420 var chrome = _window.chrome;
1421 var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
1422 var hasPushState =
1423 !isChromePackagedApp && _window.history && history.pushState;
1424 if (autoBreadcrumbs.location && hasPushState) {
1425 // TODO: remove onpopstate handler on uninstall()
1426 var oldOnPopState = _window.onpopstate;
1427 _window.onpopstate = function() {
1428 var currentHref = self._location.href;
1429 self._captureUrlChange(self._lastHref, currentHref);
1430
1431 if (oldOnPopState) {
1432 return oldOnPopState.apply(this, arguments);
1433 }
1434 };
1435
1436 fill(
1437 history,
1438 'pushState',
1439 function(origPushState) {
1440 // note history.pushState.length is 0; intentionally not declaring
1441 // params to preserve 0 arity
1442 return function(/* state, title, url */) {
1443 var url = arguments.length > 2 ? arguments[2] : undefined;
1444
1445 // url argument is optional
1446 if (url) {
1447 // coerce to string (this is what pushState does)
1448 self._captureUrlChange(self._lastHref, url + '');
1449 }
1450
1451 return origPushState.apply(this, arguments);
1452 };
1453 },
1454 wrappedBuiltIns
1455 );
1456 }
1457 1273
1458 if (autoBreadcrumbs.console && 'console' in _window && console.log) {1274 if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) {
1459 // console1275 fill(
1460 var consoleMethodCallback = function(msg, data) {1276 xhr,
1461 self.captureBreadcrumb({1277 'onreadystatechange',
1462 message: msg,1278 function(orig) {
1463 level: data.level,1279 return self.wrap(orig, undefined, onreadystatechangeHandler);
1464 category: 'console'1280 } /* intentionally don't track this instrumentation */
1465 });1281 );
1466 };1282 } else {
1467 1283 // if onreadystatechange wasn't actually set by the page on this xhr, we
1468 each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) {1284 // are free to set our own and capture the breadcrumb
1469 wrapConsoleMethod(console, level, consoleMethodCallback);1285 xhr.onreadystatechange = onreadystatechangeHandler;
1470 });1286 }
1471 }1287
1472 },1288 return origSend.apply(this, arguments);
1289 };
1290 },
1291 wrappedBuiltIns
1292 );
1293 }
1473 1294
1474 _restoreBuiltIns: function() {1295 if (autoBreadcrumbs.xhr && 'fetch' in _window) {
1475 // restore any wrapped builtins1296 fill(
1476 var builtin;1297 _window,
1477 while (this._wrappedBuiltIns.length) {1298 'fetch',
1478 builtin = this._wrappedBuiltIns.shift();1299 function(origFetch) {
1300 return function(fn, t) {
1301 // preserve arity
1302 // Make a copy of the arguments to prevent deoptimization
1303 // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments
1304 var args = new Array(arguments.length);
1305 for (var i = 0; i < args.length; ++i) {
1306 args[i] = arguments[i];
1307 }
1479 1308
1480 var obj = builtin[0],1309 var fetchInput = args[0];
1481 name = builtin[1],1310 var method = 'GET';
1482 orig = builtin[2];1311 var url;
1483 1312
1484 obj[name] = orig;1313 if (typeof fetchInput === 'string') {
1485 }1314 url = fetchInput;
1486 },1315 } else if ('Request' in _window && fetchInput instanceof _window.Request) {
1316 url = fetchInput.url;
1317 if (fetchInput.method) {
1318 method = fetchInput.method;
1319 }
1320 } else {
1321 url = '' + fetchInput;
1322 }
1487 1323
1488 _drainPlugins: function() {1324 if (args[1] && args[1].method) {
1489 var self = this;1325 method = args[1].method;
1326 }
1490 1327
1491 // FIX ME TODO1328 var fetchData = {
1492 each(this._plugins, function(_, plugin) {1329 method: method,
1493 var installer = plugin[0];1330 url: url,
1494 var args = plugin[1];1331 status_code: null
1495 installer.apply(self, [self].concat(args));1332 };
1496 });
1497 },
1498 1333
1499 _parseDSN: function(str) {1334 self.captureBreadcrumb({
1500 var m = dsnPattern.exec(str),1335 type: 'http',
1501 dsn = {},1336 category: 'fetch',
1502 i = 7;1337 data: fetchData
1338 });
1503 1339
1504 try {1340 return origFetch.apply(this, args).then(function(response) {
1505 while (i--) dsn[dsnKeys[i]] = m[i] || '';1341 fetchData.status_code = response.status;
1506 } catch (e) {
1507 throw new RavenConfigError('Invalid DSN: ' + str);
1508 }
1509 1342
1510 if (dsn.pass && !this._globalOptions.allowSecretKey) {1343 return response;
1511 throw new RavenConfigError(1344 });
1512 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key'1345 };
1513 );1346 },
1514 }1347 wrappedBuiltIns
1348 );
1349 }
1515 1350
1516 return dsn;1351 // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
1517 },1352 // to the document. Do this before we instrument addEventListener.
1353 if (autoBreadcrumbs.dom && this._hasDocument) {
1354 if (_document.addEventListener) {
1355 _document.addEventListener('click', self._breadcrumbEventHandler('click'), false);
1356 _document.addEventListener('keypress', self._keypressEventHandler(), false);
1357 } else {
1358 // IE8 Compatibility
1359 _document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
1360 _document.attachEvent('onkeypress', self._keypressEventHandler());
1361 }
1362 }
1518 1363
1519 _getGlobalServer: function(uri) {1364 // record navigation (URL) changes
1520 // assemble the endpoint from the uri pieces1365 // NOTE: in Chrome App environment, touching history.pushState, *even inside
1521 var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : '');1366 // a try/catch block*, will cause Chrome to output an error to console.error
1367 // borrowed from: https://github.com/angular/angular.js/pull/13945/files
1368 var chrome = _window.chrome;
1369 var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
1370 var hasPushAndReplaceState =
1371 !isChromePackagedApp &&
1372 _window.history &&
1373 history.pushState &&
1374 history.replaceState;
1375 if (autoBreadcrumbs.location && hasPushAndReplaceState) {
1376 // TODO: remove onpopstate handler on uninstall()
1377 var oldOnPopState = _window.onpopstate;
1378 _window.onpopstate = function() {
1379 var currentHref = self._location.href;
1380 self._captureUrlChange(self._lastHref, currentHref);
1381
1382 if (oldOnPopState) {
1383 return oldOnPopState.apply(this, arguments);
1384 }
1385 };
1386
1387 var historyReplacementFunction = function(origHistFunction) {
1388 // note history.pushState.length is 0; intentionally not declaring
1389 // params to preserve 0 arity
1390 return function(/* state, title, url */) {
1391 var url = arguments.length > 2 ? arguments[2] : undefined;
1392
1393 // url argument is optional
1394 if (url) {
1395 // coerce to string (this is what pushState does)
1396 self._captureUrlChange(self._lastHref, url + '');
1397 }
1522 1398
1523 if (uri.protocol) {1399 return origHistFunction.apply(this, arguments);
1524 globalServer = uri.protocol + ':' + globalServer;1400 };
1525 }1401 };
1526 return globalServer;
1527 },
1528 1402
1529 _handleOnErrorStackInfo: function() {1403 fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns);
1530 // if we are intentionally ignoring errors via onerror, bail out1404 fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns);
1531 if (!this._ignoreOnError) {1405 }
1532 this._handleStackInfo.apply(this, arguments);
1533 }
1534 },
1535 1406
1536 _handleStackInfo: function(stackInfo, options) {1407 if (autoBreadcrumbs.console && 'console' in _window && console.log) {
1537 var frames = this._prepareFrames(stackInfo, options);1408 // console
1409 var consoleMethodCallback = function(msg, data) {
1410 self.captureBreadcrumb({
1411 message: msg,
1412 level: data.level,
1413 category: 'console'
1414 });
1415 };
1416
1417 each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) {
1418 wrapConsoleMethod(console, level, consoleMethodCallback);
1419 });
1420 }
1421 },
1538 1422
1539 this._triggerEvent('handle', {1423 _restoreBuiltIns: function() {
1540 stackInfo: stackInfo,1424 // restore any wrapped builtins
1541 options: options1425 var builtin;
1542 });1426 while (this._wrappedBuiltIns.length) {
1427 builtin = this._wrappedBuiltIns.shift();
1543 1428
1544 this._processException(1429 var obj = builtin[0],
1545 stackInfo.name,1430 name = builtin[1],
1546 stackInfo.message,1431 orig = builtin[2];
1547 stackInfo.url,
1548 stackInfo.lineno,
1549 frames,
1550 options
1551 );
1552 },
1553
1554 _prepareFrames: function(stackInfo, options) {
1555 var self = this;
1556 var frames = [];
1557 if (stackInfo.stack && stackInfo.stack.length) {
1558 each(stackInfo.stack, function(i, stack) {
1559 var frame = self._normalizeFrame(stack, stackInfo.url);
1560 if (frame) {
1561 frames.push(frame);
1562 }
1563 });
1564
1565 // e.g. frames captured via captureMessage throw
1566 if (options && options.trimHeadFrames) {
1567 for (
1568 var j = 0;
1569 j < options.trimHeadFrames && j < frames.length;
1570 j++
1571 ) {
1572 frames[j].in_app = false;
1573 }
1574 }
1575 }
1576 frames = frames.slice(0, this._globalOptions.stackTraceLimit);
1577 return frames;
1578 },
1579
1580 _normalizeFrame: function(frame, stackInfoUrl) {
1581 // normalize the frames data
1582 var normalized = {
1583 filename: frame.url,
1584 lineno: frame.line,
1585 colno: frame.column,
1586 function: frame.func || '?'
1587 };
1588 1432
1589 // Case when we don't have any information about the error1433 obj[name] = orig;
1590 // E.g. throwing a string or raw object, instead of an `Error` in Firefox1434 }
1591 // Generating synthetic error doesn't add any value here1435 },
1592 //1436
1593 // We should probably somehow let a user know that they should fix their code1437 _drainPlugins: function() {
1594 if (!frame.url) {1438 var self = this;
1595 normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler1439
1596 }1440 // FIX ME TODO
1441 each(this._plugins, function(_, plugin) {
1442 var installer = plugin[0];
1443 var args = plugin[1];
1444 installer.apply(self, [self].concat(args));
1445 });
1446 },
1447
1448 _parseDSN: function(str) {
1449 var m = dsnPattern.exec(str),
1450 dsn = {},
1451 i = 7;
1452
1453 try {
1454 while (i--) dsn[dsnKeys[i]] = m[i] || '';
1455 } catch (e) {
1456 throw new RavenConfigError('Invalid DSN: ' + str);
1457 }
1597 1458
1598 normalized.in_app = !// determine if an exception came from outside of our app1459 if (dsn.pass && !this._globalOptions.allowSecretKey) {
1599 // first we check the global includePaths list.1460 throw new RavenConfigError(
1600 (1461 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key'
1601 (!!this._globalOptions.includePaths.test &&1462 );
1602 !this._globalOptions.includePaths.test(normalized.filename)) ||1463 }
1603 // Now we check for fun, if the function name is Raven or TraceKit
1604 /(Raven|TraceKit)\./.test(normalized['function']) ||
1605 // finally, we do a last ditch effort and check for raven.min.js
1606 /raven\.(min\.)?js$/.test(normalized.filename)
1607 );
1608
1609 return normalized;
1610 },
1611
1612 _processException: function(
1613 type,
1614 message,
1615 fileurl,
1616 lineno,
1617 frames,
1618 options
1619 ) {
1620 var testString = (type || '') + ': ' + (message || '');
1621
1622 if (
1623 !!this._globalOptions.ignoreErrors.test &&
1624 this._globalOptions.ignoreErrors.test(testString)
1625 )
1626 return;
1627
1628 var stacktrace;
1629
1630 if (frames && frames.length) {
1631 fileurl = frames[0].filename || fileurl;
1632 // Sentry expects frames oldest to newest
1633 // and JS sends them as newest to oldest
1634 frames.reverse();
1635 stacktrace = {frames: frames};
1636 } else if (fileurl) {
1637 stacktrace = {
1638 frames: [
1639 {
1640 filename: fileurl,
1641 lineno: lineno,
1642 in_app: true
1643 }
1644 ]
1645 };
1646 }
1647 1464
1648 if (1465 return dsn;
1649 !!this._globalOptions.ignoreUrls.test &&1466 },
1650 this._globalOptions.ignoreUrls.test(fileurl)
1651 )
1652 return;
1653 if (
1654 !!this._globalOptions.whitelistUrls.test &&
1655 !this._globalOptions.whitelistUrls.test(fileurl)
1656 )
1657 return;
1658
1659 var data = objectMerge(
1660 {
1661 // sentry.interfaces.Exception
1662 exception: {
1663 values: [
1664 {
1665 type: type,
1666 value: message,
1667 stacktrace: stacktrace
1668 }
1669 ]
1670 },
1671 culprit: fileurl
1672 },
1673 options
1674 );
1675
1676 // Fire away!
1677 this._send(data);
1678 },
1679
1680 _trimPacket: function(data) {
1681 // For now, we only want to truncate the two different messages
1682 // but this could/should be expanded to just trim everything
1683 var max = this._globalOptions.maxMessageLength;
1684 if (data.message) {
1685 data.message = truncate(data.message, max);
1686 }
1687 if (data.exception) {
1688 var exception = data.exception.values[0];
1689 exception.value = truncate(exception.value, max);
1690 }
1691 1467
1692 var request = data.request;1468 _getGlobalServer: function(uri) {
1693 if (request) {1469 // assemble the endpoint from the uri pieces
1694 if (request.url) {1470 var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : '');
1695 request.url = truncate(request.url, this._globalOptions.maxUrlLength);1471
1696 }1472 if (uri.protocol) {
1697 if (request.Referer) {1473 globalServer = uri.protocol + ':' + globalServer;
1698 request.Referer = truncate(1474 }
1699 request.Referer,1475 return globalServer;
1700 this._globalOptions.maxUrlLength1476 },
1701 );1477
1702 }1478 _handleOnErrorStackInfo: function() {
1703 }1479 // if we are intentionally ignoring errors via onerror, bail out
1480 if (!this._ignoreOnError) {
1481 this._handleStackInfo.apply(this, arguments);
1482 }
1483 },
1484
1485 _handleStackInfo: function(stackInfo, options) {
1486 var frames = this._prepareFrames(stackInfo, options);
1487
1488 this._triggerEvent('handle', {
1489 stackInfo: stackInfo,
1490 options: options
1491 });
1492
1493 this._processException(
1494 stackInfo.name,
1495 stackInfo.message,
1496 stackInfo.url,
1497 stackInfo.lineno,
1498 frames,
1499 options
1500 );
1501 },
1502
1503 _prepareFrames: function(stackInfo, options) {
1504 var self = this;
1505 var frames = [];
1506 if (stackInfo.stack && stackInfo.stack.length) {
1507 each(stackInfo.stack, function(i, stack) {
1508 var frame = self._normalizeFrame(stack, stackInfo.url);
1509 if (frame) {
1510 frames.push(frame);
1511 }
1512 });
1513
1514 // e.g. frames captured via captureMessage throw
1515 if (options && options.trimHeadFrames) {
1516 for (var j = 0; j < options.trimHeadFrames && j < frames.length; j++) {
1517 frames[j].in_app = false;
1518 }
1519 }
1520 }
1521 frames = frames.slice(0, this._globalOptions.stackTraceLimit);
1522 return frames;
1523 },
1524
1525 _normalizeFrame: function(frame, stackInfoUrl) {
1526 // normalize the frames data
1527 var normalized = {
1528 filename: frame.url,
1529 lineno: frame.line,
1530 colno: frame.column,
1531 function: frame.func || '?'
1532 };
1533
1534 // Case when we don't have any information about the error
1535 // E.g. throwing a string or raw object, instead of an `Error` in Firefox
1536 // Generating synthetic error doesn't add any value here
1537 //
1538 // We should probably somehow let a user know that they should fix their code
1539 if (!frame.url) {
1540 normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler
1541 }
1542
1543 normalized.in_app = !// determine if an exception came from outside of our app
1544 // first we check the global includePaths list.
1545 (
1546 (!!this._globalOptions.includePaths.test &&
1547 !this._globalOptions.includePaths.test(normalized.filename)) ||
1548 // Now we check for fun, if the function name is Raven or TraceKit
1549 /(Raven|TraceKit)\./.test(normalized['function']) ||
1550 // finally, we do a last ditch effort and check for raven.min.js
1551 /raven\.(min\.)?js$/.test(normalized.filename)
1552 );
1553
1554 return normalized;
1555 },
1556
1557 _processException: function(type, message, fileurl, lineno, frames, options) {
1558 var prefixedMessage = (type ? type + ': ' : '') + (message || '');
1559 if (
1560 !!this._globalOptions.ignoreErrors.test &&
1561 (this._globalOptions.ignoreErrors.test(message) ||
1562 this._globalOptions.ignoreErrors.test(prefixedMessage))
1563 ) {
1564 return;
1565 }
1566
1567 var stacktrace;
1568
1569 if (frames && frames.length) {
1570 fileurl = frames[0].filename || fileurl;
1571 // Sentry expects frames oldest to newest
1572 // and JS sends them as newest to oldest
1573 frames.reverse();
1574 stacktrace = {frames: frames};
1575 } else if (fileurl) {
1576 stacktrace = {
1577 frames: [
1578 {
1579 filename: fileurl,
1580 lineno: lineno,
1581 in_app: true
1582 }
1583 ]
1584 };
1585 }
1586
1587 if (
1588 !!this._globalOptions.ignoreUrls.test &&
1589 this._globalOptions.ignoreUrls.test(fileurl)
1590 ) {
1591 return;
1592 }
1704 1593
1705 if (data.breadcrumbs && data.breadcrumbs.values)1594 if (
1706 this._trimBreadcrumbs(data.breadcrumbs);1595 !!this._globalOptions.whitelistUrls.test &&
1596 !this._globalOptions.whitelistUrls.test(fileurl)
1597 ) {
1598 return;
1599 }
1600
1601 var data = objectMerge(
1602 {
1603 // sentry.interfaces.Exception
1604 exception: {
1605 values: [
1606 {
1607 type: type,
1608 value: message,
1609 stacktrace: stacktrace
1610 }
1611 ]
1612 },
1613 culprit: fileurl
1614 },
1615 options
1616 );
1617
1618 // Fire away!
1619 this._send(data);
1620 },
1621
1622 _trimPacket: function(data) {
1623 // For now, we only want to truncate the two different messages
1624 // but this could/should be expanded to just trim everything
1625 var max = this._globalOptions.maxMessageLength;
1626 if (data.message) {
1627 data.message = truncate(data.message, max);
1628 }
1629 if (data.exception) {
1630 var exception = data.exception.values[0];
1631 exception.value = truncate(exception.value, max);
1632 }
1707 1633
1708 return data;1634 var request = data.request;
1709 },1635 if (request) {
1636 if (request.url) {
1637 request.url = truncate(request.url, this._globalOptions.maxUrlLength);
1638 }
1639 if (request.Referer) {
1640 request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength);
1641 }
1642 }
1710 1643
1711 /**1644 if (data.breadcrumbs && data.breadcrumbs.values)
1645 this._trimBreadcrumbs(data.breadcrumbs);
1646
1647 return data;
1648 },
1649
1650 /**
1712 * Truncate breadcrumb values (right now just URLs)1651 * Truncate breadcrumb values (right now just URLs)
1713 */1652 */
1714 _trimBreadcrumbs: function(breadcrumbs) {1653 _trimBreadcrumbs: function(breadcrumbs) {
1715 // known breadcrumb properties with urls1654 // known breadcrumb properties with urls
1716 // TODO: also consider arbitrary prop values that start with (https?)?://1655 // TODO: also consider arbitrary prop values that start with (https?)?://
1717 var urlProps = ['to', 'from', 'url'],1656 var urlProps = ['to', 'from', 'url'],
1718 urlProp,1657 urlProp,
1719 crumb,1658 crumb,
1720 data;1659 data;
1721 1660
1722 for (var i = 0; i < breadcrumbs.values.length; ++i) {1661 for (var i = 0; i < breadcrumbs.values.length; ++i) {
1723 crumb = breadcrumbs.values[i];1662 crumb = breadcrumbs.values[i];
1724 if (1663 if (
1725 !crumb.hasOwnProperty('data') ||1664 !crumb.hasOwnProperty('data') ||
1726 !isObject(crumb.data) ||1665 !isObject(crumb.data) ||
1727 objectFrozen(crumb.data)1666 objectFrozen(crumb.data)
1728 )1667 )
1729 continue;1668 continue;
1730 1669
1731 data = objectMerge({}, crumb.data);1670 data = objectMerge({}, crumb.data);
1732 for (var j = 0; j < urlProps.length; ++j) {1671 for (var j = 0; j < urlProps.length; ++j) {
1733 urlProp = urlProps[j];1672 urlProp = urlProps[j];
1734 if (data.hasOwnProperty(urlProp) && data[urlProp]) {1673 if (data.hasOwnProperty(urlProp) && data[urlProp]) {
1735 data[urlProp] = truncate(1674 data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength);
1736 data[urlProp],1675 }
1737 this._globalOptions.maxUrlLength1676 }
1738 );1677 breadcrumbs.values[i].data = data;
1739 }1678 }
1740 }1679 },
1741 breadcrumbs.values[i].data = data;
1742 }
1743 },
1744 1680
1745 _getHttpData: function() {1681 _getHttpData: function() {
1746 if (!this._hasNavigator && !this._hasDocument) return;1682 if (!this._hasNavigator && !this._hasDocument) return;
1747 var httpData = {};1683 var httpData = {};
1748 1684
1749 if (this._hasNavigator && _navigator.userAgent) {1685 if (this._hasNavigator && _navigator.userAgent) {
1750 httpData.headers = {1686 httpData.headers = {
1751 'User-Agent': navigator.userAgent1687 'User-Agent': navigator.userAgent
1752 };1688 };
1753 }1689 }
1754 1690
1755 if (this._hasDocument) {1691 if (this._hasDocument) {
1756 if (_document.location && _document.location.href) {1692 if (_document.location && _document.location.href) {
1757 httpData.url = _document.location.href;1693 httpData.url = _document.location.href;
1758 }1694 }
1759 if (_document.referrer) {1695 if (_document.referrer) {
1760 if (!httpData.headers) httpData.headers = {};1696 if (!httpData.headers) httpData.headers = {};
1761 httpData.headers.Referer = _document.referrer;1697 httpData.headers.Referer = _document.referrer;
1762 }1698 }
1763 }1699 }
1764 1700
1765 return httpData;1701 return httpData;
1766 },1702 },
1767 1703
1768 _resetBackoff: function() {1704 _resetBackoff: function() {
1769 this._backoffDuration = 0;1705 this._backoffDuration = 0;
1770 this._backoffStart = null;1706 this._backoffStart = null;
1771 },1707 },
1772 1708
1773 _shouldBackoff: function() {1709 _shouldBackoff: function() {
1774 return (1710 return this._backoffDuration && now() - this._backoffStart < this._backoffDuration;
1775 this._backoffDuration &&1711 },
1776 now() - this._backoffStart < this._backoffDuration
1777 );
1778 },
1779 1712
1780 /**1713 /**
1781 * Returns true if the in-process data payload matches the signature1714 * Returns true if the in-process data payload matches the signature
1782 * of the previously-sent data1715 * of the previously-sent data
1783 *1716 *
@@ -1786,362 +1719,449 @@ · 2 unmodified lines
1786 * other old browsers). This can take the form of an "exception"1719 * other old browsers). This can take the form of an "exception"
1787 * data object with a single frame (derived from the onerror args).1720 * data object with a single frame (derived from the onerror args).
1788 */1721 */
1789 _isRepeatData: function(current) {1722 _isRepeatData: function(current) {
1790 var last = this._lastData;1723 var last = this._lastData;
1791 1724
1792 if (1725 if (
1793 !last ||1726 !last ||
1794 current.message !== last.message || // defined for captureMessage1727 current.message !== last.message || // defined for captureMessage
1795 current.culprit !== last.culprit // defined for captureException/onerror1728 current.culprit !== last.culprit // defined for captureException/onerror
1796 )1729 )
1797 return false;1730 return false;
1798 1731
1799 // Stacktrace interface (i.e. from captureMessage)1732 // Stacktrace interface (i.e. from captureMessage)
1800 if (current.stacktrace || last.stacktrace) {1733 if (current.stacktrace || last.stacktrace) {
1801 return isSameStacktrace(current.stacktrace, last.stacktrace);1734 return isSameStacktrace(current.stacktrace, last.stacktrace);
1802 } else if (current.exception || last.exception) {1735 } else if (current.exception || last.exception) {
1803 // Exception interface (i.e. from captureException/onerror)1736 // Exception interface (i.e. from captureException/onerror)
1804 return isSameException(current.exception, last.exception);1737 return isSameException(current.exception, last.exception);
1805 }1738 }
1806 1739
1807 return true;1740 return true;
1808 },1741 },
1809 1742
1810 _setBackoffState: function(request) {1743 _setBackoffState: function(request) {
1811 // If we are already in a backoff state, don't change anything1744 // If we are already in a backoff state, don't change anything
1812 if (this._shouldBackoff()) {1745 if (this._shouldBackoff()) {
1813 return;1746 return;
1814 }1747 }
1815 1748
1816 var status = request.status;1749 var status = request.status;
1750
1751 // 400 - project_id doesn't exist or some other fatal
1752 // 401 - invalid/revoked dsn
1753 // 429 - too many requests
1754 if (!(status === 400 || status === 401 || status === 429)) return;
1755
1756 var retry;
1757 try {
1758 // If Retry-After is not in Access-Control-Expose-Headers, most
1759 // browsers will throw an exception trying to access it
1760 retry = request.getResponseHeader('Retry-After');
1761 retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds
1762 } catch (e) {
1763 /* eslint no-empty:0 */
1764 }
1817 1765
1818 // 400 - project_id doesn't exist or some other fatal1766 this._backoffDuration = retry
1819 // 401 - invalid/revoked dsn1767 ? // If Sentry server returned a Retry-After value, use it
1820 // 429 - too many requests1768 retry
1821 if (!(status === 400 || status === 401 || status === 429)) return;1769 : // Otherwise, double the last backoff duration (starts at 1 sec)
1770 this._backoffDuration * 2 || 1000;
1822 1771
1823 var retry;1772 this._backoffStart = now();
1824 try {1773 },
1825 // If Retry-After is not in Access-Control-Expose-Headers, most
1826 // browsers will throw an exception trying to access it
1827 retry = request.getResponseHeader('Retry-After');
1828 retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds
1829 } catch (e) {
1830 /* eslint no-empty:0 */
1831 }
1832 1774
1833 this._backoffDuration = retry1775 _send: function(data) {
1834 ? // If Sentry server returned a Retry-After value, use it1776 var globalOptions = this._globalOptions;
1835 retry
1836 : // Otherwise, double the last backoff duration (starts at 1 sec)
1837 this._backoffDuration * 2 || 1000;
1838 1777
1839 this._backoffStart = now();1778 var baseData = {
1840 },1779 project: this._globalProject,
1780 logger: globalOptions.logger,
1781 platform: 'javascript'
1782 },
1783 httpData = this._getHttpData();
1841 1784
1842 _send: function(data) {1785 if (httpData) {
1843 var globalOptions = this._globalOptions;1786 baseData.request = httpData;
1787 }
1844 1788
1845 var baseData = {1789 // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload
1846 project: this._globalProject,1790 if (data.trimHeadFrames) delete data.trimHeadFrames;
1847 logger: globalOptions.logger,
1848 platform: 'javascript'
1849 },
1850 httpData = this._getHttpData();
1851 1791
1852 if (httpData) {1792 data = objectMerge(baseData, data);
1853 baseData.request = httpData;
1854 }
1855 1793
1856 // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload1794 // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
1857 if (data.trimHeadFrames) delete data.trimHeadFrames;1795 data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags);
1858 1796 data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra);
1859 data = objectMerge(baseData, data);
1860
1861 // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
1862 data.tags = objectMerge(
1863 objectMerge({}, this._globalContext.tags),
1864 data.tags
1865 );
1866 data.extra = objectMerge(
1867 objectMerge({}, this._globalContext.extra),
1868 data.extra
1869 );
1870
1871 // Send along our own collected metadata with extra
1872 data.extra['session:duration'] = now() - this._startTime;
1873
1874 if (this._breadcrumbs && this._breadcrumbs.length > 0) {
1875 // intentionally make shallow copy so that additions
1876 // to breadcrumbs aren't accidentally sent in this request
1877 data.breadcrumbs = {
1878 values: [].slice.call(this._breadcrumbs, 0)
1879 };
1880 }
1881 1797
1882 // If there are no tags/extra, strip the key from the payload alltogther.1798 // Send along our own collected metadata with extra
1883 if (isEmptyObject(data.tags)) delete data.tags;1799 data.extra['session:duration'] = now() - this._startTime;
1884 1800
1885 if (this._globalContext.user) {1801 if (this._breadcrumbs && this._breadcrumbs.length > 0) {
1886 // sentry.interfaces.User1802 // intentionally make shallow copy so that additions
1887 data.user = this._globalContext.user;1803 // to breadcrumbs aren't accidentally sent in this request
1888 }1804 data.breadcrumbs = {
1805 values: [].slice.call(this._breadcrumbs, 0)
1806 };
1807 }
1889 1808
1890 // Include the environment if it's defined in globalOptions1809 // If there are no tags/extra, strip the key from the payload alltogther.
1891 if (globalOptions.environment)1810 if (isEmptyObject(data.tags)) delete data.tags;
1892 data.environment = globalOptions.environment;
1893 1811
1894 // Include the release if it's defined in globalOptions1812 if (this._globalContext.user) {
1895 if (globalOptions.release) data.release = globalOptions.release;1813 // sentry.interfaces.User
1814 data.user = this._globalContext.user;
1815 }
1896 1816
1897 // Include server_name if it's defined in globalOptions1817 // Include the environment if it's defined in globalOptions
1898 if (globalOptions.serverName) data.server_name = globalOptions.serverName;1818 if (globalOptions.environment) data.environment = globalOptions.environment;
1899 1819
1900 if (isFunction(globalOptions.dataCallback)) {1820 // Include the release if it's defined in globalOptions
1901 data = globalOptions.dataCallback(data) || data;1821 if (globalOptions.release) data.release = globalOptions.release;
1902 }
1903 1822
1904 // Why??????????1823 // Include server_name if it's defined in globalOptions
1905 if (!data || isEmptyObject(data)) {1824 if (globalOptions.serverName) data.server_name = globalOptions.serverName;
1906 return;
1907 }
1908 1825
1909 // Check if the request should be filtered or not1826 if (isFunction(globalOptions.dataCallback)) {
1910 if (1827 data = globalOptions.dataCallback(data) || data;
1911 isFunction(globalOptions.shouldSendCallback) &&1828 }
1912 !globalOptions.shouldSendCallback(data)
1913 ) {
1914 return;
1915 }
1916 1829
1917 // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests),1830 // Why??????????
1918 // so drop requests until "cool-off" period has elapsed.1831 if (!data || isEmptyObject(data)) {
1919 if (this._shouldBackoff()) {1832 return;
1920 this._logDebug('warn', 'Raven dropped error due to backoff: ', data);1833 }
1921 return;
1922 }
1923 1834
1924 if (typeof globalOptions.sampleRate === 'number') {1835 // Check if the request should be filtered or not
1925 if (Math.random() < globalOptions.sampleRate) {1836 if (
1926 this._sendProcessedPayload(data);1837 isFunction(globalOptions.shouldSendCallback) &&
1927 }1838 !globalOptions.shouldSendCallback(data)
1928 } else {1839 ) {
1929 this._sendProcessedPayload(data);1840 return;
1930 }1841 }
1931 },1842
1843 // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests),
1844 // so drop requests until "cool-off" period has elapsed.
1845 if (this._shouldBackoff()) {
1846 this._logDebug('warn', 'Raven dropped error due to backoff: ', data);
1847 return;
1848 }
1932 1849
1933 _getUuid: function() {1850 if (typeof globalOptions.sampleRate === 'number') {
1934 return uuid4();1851 if (Math.random() < globalOptions.sampleRate) {
1935 },1852 this._sendProcessedPayload(data);
1853 }
1854 } else {
1855 this._sendProcessedPayload(data);
1856 }
1857 },
1936 1858
1937 _sendProcessedPayload: function(data, callback) {1859 _getUuid: function() {
1938 var self = this;1860 return uuid4();
1939 var globalOptions = this._globalOptions;1861 },
1940 1862
1941 if (!this.isSetup()) return;1863 _sendProcessedPayload: function(data, callback) {
1864 var self = this;
1865 var globalOptions = this._globalOptions;
1942 1866
1943 // Try and clean up the packet before sending by truncating long values1867 if (!this.isSetup()) return;
1944 data = this._trimPacket(data);
1945 1868
1946 // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback,1869 // Try and clean up the packet before sending by truncating long values
1947 // but this would require copying an un-truncated copy of the data packet, which can be1870 data = this._trimPacket(data);
1948 // arbitrarily deep (extra_data) -- could be worthwhile? will revisit
1949 if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) {
1950 this._logDebug('warn', 'Raven dropped repeat event: ', data);
1951 return;
1952 }
1953 1871
1954 // Send along an event_id if not explicitly passed.1872 // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback,
1955 // This event_id can be used to reference the error within Sentry itself.1873 // but this would require copying an un-truncated copy of the data packet, which can be
1956 // Set lastEventId after we know the error should actually be sent1874 // arbitrarily deep (extra_data) -- could be worthwhile? will revisit
1957 this._lastEventId = data.event_id || (data.event_id = this._getUuid());1875 if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) {
1876 this._logDebug('warn', 'Raven dropped repeat event: ', data);
1877 return;
1878 }
1958 1879
1959 // Store outbound payload after trim1880 // Send along an event_id if not explicitly passed.
1960 this._lastData = data;1881 // This event_id can be used to reference the error within Sentry itself.
1882 // Set lastEventId after we know the error should actually be sent
1883 this._lastEventId = data.event_id || (data.event_id = this._getUuid());
1961 1884
1962 this._logDebug('debug', 'Raven about to send:', data);1885 // Store outbound payload after trim
1886 this._lastData = data;
1963 1887
1964 var auth = {1888 this._logDebug('debug', 'Raven about to send:', data);
1965 sentry_version: '7',
1966 sentry_client: 'raven-js/' + this.VERSION,
1967 sentry_key: this._globalKey
1968 };
1969 1889
1970 if (this._globalSecret) {1890 var auth = {
1971 auth.sentry_secret = this._globalSecret;1891 sentry_version: '7',
1972 }1892 sentry_client: 'raven-js/' + this.VERSION,
1893 sentry_key: this._globalKey
1894 };
1973 1895
1974 var exception = data.exception && data.exception.values[0];1896 if (this._globalSecret) {
1975 this.captureBreadcrumb({1897 auth.sentry_secret = this._globalSecret;
1976 category: 'sentry',1898 }
1977 message: exception
1978 ? (exception.type ? exception.type + ': ' : '') + exception.value
1979 : data.message,
1980 event_id: data.event_id,
1981 level: data.level || 'error' // presume error unless specified
1982 });
1983 1899
1984 var url = this._globalEndpoint;1900 var exception = data.exception && data.exception.values[0];
1985 (globalOptions.transport || this._makeRequest).call(this, {1901
1986 url: url,1902 // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy
1987 auth: auth,1903 if (
1988 data: data,1904 this._globalOptions.autoBreadcrumbs &&
1989 options: globalOptions,1905 this._globalOptions.autoBreadcrumbs.sentry
1990 onSuccess: function success() {1906 ) {
1991 self._resetBackoff();1907 this.captureBreadcrumb({
1992 1908 category: 'sentry',
1993 self._triggerEvent('success', {1909 message: exception
1994 data: data,1910 ? (exception.type ? exception.type + ': ' : '') + exception.value
1995 src: url1911 : data.message,
1996 });1912 event_id: data.event_id,
1997 callback && callback();1913 level: data.level || 'error' // presume error unless specified
1998 },1914 });
1999 onError: function failure(error) {1915 }
2000 self._logDebug('error', 'Raven transport failed to send: ', error);
2001
2002 if (error.request) {
2003 self._setBackoffState(error.request);
2004 }
2005
2006 self._triggerEvent('failure', {
2007 data: data,
2008 src: url
2009 });
2010 error =
2011 error ||
2012 new Error('Raven send failed (no additional details provided)');
2013 callback && callback(error);
2014 }
2015 });
2016 },
2017
2018 _makeRequest: function(opts) {
2019 var request = _window.XMLHttpRequest && new _window.XMLHttpRequest();
2020 if (!request) return;
2021
2022 // if browser doesn't support CORS (e.g. IE7), we are out of luck
2023 var hasCORS =
2024 'withCredentials' in request || typeof XDomainRequest !== 'undefined';
2025
2026 if (!hasCORS) return;
2027
2028 var url = opts.url;
2029
2030 if ('withCredentials' in request) {
2031 request.onreadystatechange = function() {
2032 if (request.readyState !== 4) {
2033 return;
2034 } else if (request.status === 200) {
2035 opts.onSuccess && opts.onSuccess();
2036 } else if (opts.onError) {
2037 var err = new Error('Sentry error code: ' + request.status);
2038 err.request = request;
2039 opts.onError(err);
2040 }
2041 };
2042 } else {
2043 request = new XDomainRequest();
2044 // xdomainrequest cannot go http -> https (or vice versa),
2045 // so always use protocol relative
2046 url = url.replace(/^https?:/, '');
2047
2048 // onreadystatechange not supported by XDomainRequest
2049 if (opts.onSuccess) {
2050 request.onload = opts.onSuccess;
2051 }
2052 if (opts.onError) {
2053 request.onerror = function() {
2054 var err = new Error('Sentry error code: XDomainRequest');
2055 err.request = request;
2056 opts.onError(err);
2057 };
2058 }
2059 }
2060 1916
2061 // NOTE: auth is intentionally sent as part of query string (NOT as custom1917 var url = this._globalEndpoint;
2062 // HTTP header) so as to avoid preflight CORS requests1918 (globalOptions.transport || this._makeRequest).call(this, {
2063 request.open('POST', url + '?' + urlencode(opts.auth));1919 url: url,
2064 request.send(stringify(opts.data));1920 auth: auth,
2065 },1921 data: data,
2066 1922 options: globalOptions,
2067 _logDebug: function(level) {1923 onSuccess: function success() {
2068 if (this._originalConsoleMethods[level] && this.debug) {1924 self._resetBackoff();
2069 // In IE<10 console methods do not have their own 'apply' method1925
2070 Function.prototype.apply.call(1926 self._triggerEvent('success', {
2071 this._originalConsoleMethods[level],1927 data: data,
2072 this._originalConsole,1928 src: url
2073 [].slice.call(arguments, 1)1929 });
2074 );1930 callback && callback();
2075 }1931 },
2076 },1932 onError: function failure(error) {
2077 1933 self._logDebug('error', 'Raven transport failed to send: ', error);
2078 _mergeContext: function(key, context) {1934
2079 if (isUndefined(context)) {1935 if (error.request) {
2080 delete this._globalContext[key];1936 self._setBackoffState(error.request);
2081 } else {1937 }
2082 this._globalContext[key] = objectMerge(1938
2083 this._globalContext[key] || {},1939 self._triggerEvent('failure', {
2084 context1940 data: data,
2085 );1941 src: url
2086 }1942 });
2087 }1943 error = error || new Error('Raven send failed (no additional details provided)');
2088 };1944 callback && callback(error);
1945 }
1946 });
1947 },
1948
1949 _makeRequest: function(opts) {
1950 var request = _window.XMLHttpRequest && new _window.XMLHttpRequest();
1951 if (!request) return;
1952
1953 // if browser doesn't support CORS (e.g. IE7), we are out of luck
1954 var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined';
1955
1956 if (!hasCORS) return;
1957
1958 var url = opts.url;
1959
1960 if ('withCredentials' in request) {
1961 request.onreadystatechange = function() {
1962 if (request.readyState !== 4) {
1963 return;
1964 } else if (request.status === 200) {
1965 opts.onSuccess && opts.onSuccess();
1966 } else if (opts.onError) {
1967 var err = new Error('Sentry error code: ' + request.status);
1968 err.request = request;
1969 opts.onError(err);
1970 }
1971 };
1972 } else {
1973 request = new XDomainRequest();
1974 // xdomainrequest cannot go http -> https (or vice versa),
1975 // so always use protocol relative
1976 url = url.replace(/^https?:/, '');
1977
1978 // onreadystatechange not supported by XDomainRequest
1979 if (opts.onSuccess) {
1980 request.onload = opts.onSuccess;
1981 }
1982 if (opts.onError) {
1983 request.onerror = function() {
1984 var err = new Error('Sentry error code: XDomainRequest');
1985 err.request = request;
1986 opts.onError(err);
1987 };
1988 }
1989 }
1990
1991 // NOTE: auth is intentionally sent as part of query string (NOT as custom
1992 // HTTP header) so as to avoid preflight CORS requests
1993 request.open('POST', url + '?' + urlencode(opts.auth));
1994 request.send(stringify(opts.data));
1995 },
1996
1997 _logDebug: function(level) {
1998 if (this._originalConsoleMethods[level] && this.debug) {
1999 // In IE<10 console methods do not have their own 'apply' method
2000 Function.prototype.apply.call(
2001 this._originalConsoleMethods[level],
2002 this._originalConsole,
2003 [].slice.call(arguments, 1)
2004 );
2005 }
2006 },
2007
2008 _mergeContext: function(key, context) {
2009 if (isUndefined(context)) {
2010 delete this._globalContext[key];
2011 } else {
2012 this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context);
2013 }
2014 }
2015};
2016
2017// Deprecations
2018Raven.prototype.setUser = Raven.prototype.setUserContext;
2019Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
2020
2021module.exports = Raven;
2089 2022
2090 /*------------------------------------------------2023}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2091 * utils2024},{"1":1,"2":2,"5":5,"6":6,"7":7}],4:[function(_dereq_,module,exports){
2025(function (global){
2026/**
2027 * Enforces a single instance of the Raven client, and the
2028 * main entry point for Raven. If you are a consumer of the
2029 * Raven library, you SHOULD load this file (vs raven.js).
2030 **/
2031
2032var RavenConstructor = _dereq_(3);
2033
2034// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
2035var _window =
2036 typeof window !== 'undefined'
2037 ? window
2038 : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2039var _Raven = _window.Raven;
2040
2041var Raven = new RavenConstructor();
2042
2043/*
2044 * Allow multiple versions of Raven to be installed.
2045 * Strip Raven from the global context and returns the instance.
2092 *2046 *
2093 * conditionally exported for test via Raven.utils2047 * @return {Raven}
2094 =================================================
2095 */2048 */
2096 var objectPrototype = Object.prototype;2049Raven.noConflict = function() {
2050 _window.Raven = _Raven;
2051 return Raven;
2052};
2053
2054Raven.afterLoad();
2055
2056module.exports = Raven;
2057
2058}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2059},{"3":3}],5:[function(_dereq_,module,exports){
2060(function (global){
2061var _window =
2062 typeof window !== 'undefined'
2063 ? window
2064 : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2065
2066function isObject(what) {
2067 return typeof what === 'object' && what !== null;
2068}
2069
2070// Yanked from https://git.io/vS8DV re-used under CC0
2071// with some tiny modifications
2072function isError(value) {
2073 switch ({}.toString.call(value)) {
2074 case '[object Error]':
2075 return true;
2076 case '[object Exception]':
2077 return true;
2078 case '[object DOMException]':
2079 return true;
2080 default:
2081 return value instanceof Error;
2082 }
2083}
2097 2084
2098 function isUndefined(what) {2085function isErrorEvent(value) {
2099 return what === void 0;2086 return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]';
2100 }2087}
2101 2088
2102 function isFunction(what) {2089function isUndefined(what) {
2103 return typeof what === 'function';2090 return what === void 0;
2104 }2091}
2105 2092
2106 function isString(what) {2093function isFunction(what) {
2107 return objectPrototype.toString.call(what) === '[object String]';2094 return typeof what === 'function';
2108 }2095}
2109 2096
2110 function isEmptyObject(what) {2097function isString(what) {
2111 for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars2098 return Object.prototype.toString.call(what) === '[object String]';
2112 return true;2099}
2113 }
2114 2100
2115 function each(obj, callback) {2101function isArray(what) {
2116 var i, j;2102 return Object.prototype.toString.call(what) === '[object Array]';
2103}
2117 2104
2118 if (isUndefined(obj.length)) {2105function isEmptyObject(what) {
2119 for (i in obj) {2106 for (var _ in what) {
2120 if (hasKey(obj, i)) {2107 if (what.hasOwnProperty(_)) {
2121 callback.call(null, i, obj[i]);2108 return false;
2122 }2109 }
2123 }2110 }
2124 } else {2111 return true;
2125 j = obj.length;2112}
2126 if (j) {2113
2127 for (i = 0; i < j; i++) {2114function supportsErrorEvent() {
2128 callback.call(null, i, obj[i]);2115 try {
2129 }2116 new ErrorEvent(''); // eslint-disable-line no-new
2130 }2117 return true;
2131 }2118 } catch (e) {
2132 }2119 return false;
2120 }
2121}
2133 2122
2134 function objectMerge(obj1, obj2) {2123function wrappedCallback(callback) {
2135 if (!obj2) {2124 function dataCallback(data, original) {
2136 return obj1;2125 var normalizedData = callback(data) || data;
2137 }2126 if (original) {
2138 each(obj2, function(key, value) {2127 return original(normalizedData) || normalizedData;
2139 obj1[key] = value;2128 }
2140 });2129 return normalizedData;
2141 return obj1;2130 }
2142 }2131
2132 return dataCallback;
2133}
2134
2135function each(obj, callback) {
2136 var i, j;
2137
2138 if (isUndefined(obj.length)) {
2139 for (i in obj) {
2140 if (hasKey(obj, i)) {
2141 callback.call(null, i, obj[i]);
2142 }
2143 }
2144 } else {
2145 j = obj.length;
2146 if (j) {
2147 for (i = 0; i < j; i++) {
2148 callback.call(null, i, obj[i]);
2149 }
2150 }
2151 }
2152}
2153
2154function objectMerge(obj1, obj2) {
2155 if (!obj2) {
2156 return obj1;
2157 }
2158 each(obj2, function(key, value) {
2159 obj1[key] = value;
2160 });
2161 return obj1;
2162}
2143 2163
2144 /**2164/**
2145 * This function is only used for react-native.2165 * This function is only used for react-native.
2146 * react-native freezes object that have already been sent over the2166 * react-native freezes object that have already been sent over the
2147 * js bridge. We need this function in order to check if the object is frozen.2167 * js bridge. We need this function in order to check if the object is frozen.
@@ -2149,398 +2169,298 @@ · 1 unmodified line
2149 * supported because it's not relevant for other "platforms". See related issue:2169 * supported because it's not relevant for other "platforms". See related issue:
2150 * https://github.com/getsentry/react-native-sentry/issues/572170 * https://github.com/getsentry/react-native-sentry/issues/57
2151 */2171 */
2152 function objectFrozen(obj) {2172function objectFrozen(obj) {
2153 if (!Object.isFrozen) {2173 if (!Object.isFrozen) {
2154 return false;2174 return false;
2155 }2175 }
2156 return Object.isFrozen(obj);2176 return Object.isFrozen(obj);
2157 }2177}
2158 2178
2159 function truncate(str, max) {2179function truncate(str, max) {
2160 return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';2180 return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
2161 }2181}
2162 2182
2163 /**2183/**
2164 * hasKey, a better form of hasOwnProperty2184 * hasKey, a better form of hasOwnProperty
2165 * Example: hasKey(MainHostObject, property) === true/false2185 * Example: hasKey(MainHostObject, property) === true/false
2166 *2186 *
2167 * @param {Object} host object to check property2187 * @param {Object} host object to check property
2168 * @param {string} key to check2188 * @param {string} key to check
2169 */2189 */
2170 function hasKey(object, key) {2190function hasKey(object, key) {
2171 return objectPrototype.hasOwnProperty.call(object, key);2191 return Object.prototype.hasOwnProperty.call(object, key);
2172 }2192}
2173 2193
2174 function joinRegExp(patterns) {2194function joinRegExp(patterns) {
2175 // Combine an array of regular expressions and strings into one large regexp2195 // Combine an array of regular expressions and strings into one large regexp
2176 // Be mad.2196 // Be mad.
2177 var sources = [],2197 var sources = [],
2178 i = 0,2198 i = 0,
2179 len = patterns.length,2199 len = patterns.length,
2180 pattern;2200 pattern;
2181 2201
2182 for (; i < len; i++) {2202 for (; i < len; i++) {
2183 pattern = patterns[i];2203 pattern = patterns[i];
2184 if (isString(pattern)) {2204 if (isString(pattern)) {
2185 // If it's a string, we need to escape it2205 // If it's a string, we need to escape it
2186 // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions2206 // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
2187 sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));2207 sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));
2188 } else if (pattern && pattern.source) {2208 } else if (pattern && pattern.source) {
2189 // If it's a regexp already, we want to extract the source2209 // If it's a regexp already, we want to extract the source
2190 sources.push(pattern.source);2210 sources.push(pattern.source);
2191 }2211 }
2192 // Intentionally skip other cases2212 // Intentionally skip other cases
2193 }2213 }
2194 return new RegExp(sources.join('|'), 'i');2214 return new RegExp(sources.join('|'), 'i');
2195 }2215}
2196 2216
2197 function urlencode(o) {2217function urlencode(o) {
2198 var pairs = [];2218 var pairs = [];
2199 each(o, function(key, value) {2219 each(o, function(key, value) {
2200 pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));2220 pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
2201 });2221 });
2202 return pairs.join('&');2222 return pairs.join('&');
2203 }2223}
2204 2224
2205 // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B2225// borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
2206 // intentionally using regex and not <a/> href parsing trick because React Native and other2226// intentionally using regex and not <a/> href parsing trick because React Native and other
2207 // environments where DOM might not be available2227// environments where DOM might not be available
2208 function parseUrl(url) {2228function parseUrl(url) {
2209 var match = url.match(2229 var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
2210 /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/2230 if (!match) return {};
2211 );2231
2212 if (!match) return {};2232 // coerce to undefined values to empty string so we don't get 'undefined'
2213 2233 var query = match[6] || '';
2214 // coerce to undefined values to empty string so we don't get 'undefined'2234 var fragment = match[8] || '';
2215 var query = match[6] || '';2235 return {
2216 var fragment = match[8] || '';2236 protocol: match[2],
2217 return {2237 host: match[4],
2218 protocol: match[2],2238 path: match[5],
2219 host: match[4],2239 relative: match[5] + query + fragment // everything minus origin
2220 path: match[5],2240 };
2221 relative: match[5] + query + fragment // everything minus origin2241}
2222 };2242function uuid4() {
2223 }2243 var crypto = _window.crypto || _window.msCrypto;
2224 function uuid4() {2244
2225 var crypto = _window.crypto || _window.msCrypto;2245 if (!isUndefined(crypto) && crypto.getRandomValues) {
2226 2246 // Use window.crypto API if available
2227 if (!isUndefined(crypto) && crypto.getRandomValues) {2247 // eslint-disable-next-line no-undef
2228 // Use window.crypto API if available2248 var arr = new Uint16Array(8);
2229 // eslint-disable-next-line no-undef2249 crypto.getRandomValues(arr);
2230 var arr = new Uint16Array(8);2250
2231 crypto.getRandomValues(arr);2251 // set 4 in byte 7
2232 2252 arr[3] = (arr[3] & 0xfff) | 0x4000;
2233 // set 4 in byte 72253 // set 2 most significant bits of byte 9 to '10'
2234 arr[3] = (arr[3] & 0xfff) | 0x4000;2254 arr[4] = (arr[4] & 0x3fff) | 0x8000;
2235 // set 2 most significant bits of byte 9 to '10'2255
2236 arr[4] = (arr[4] & 0x3fff) | 0x8000;2256 var pad = function(num) {
2237 2257 var v = num.toString(16);
2238 var pad = function(num) {2258 while (v.length < 4) {
2239 var v = num.toString(16);2259 v = '0' + v;
2240 while (v.length < 4) {2260 }
2241 v = '0' + v;2261 return v;
2242 }2262 };
2243 return v;2263
2244 };2264 return (
2245 2265 pad(arr[0]) +
2246 return (2266 pad(arr[1]) +
2247 pad(arr[0]) +2267 pad(arr[2]) +
2248 pad(arr[1]) +2268 pad(arr[3]) +
2249 pad(arr[2]) +2269 pad(arr[4]) +
2250 pad(arr[3]) +2270 pad(arr[5]) +
2251 pad(arr[4]) +2271 pad(arr[6]) +
2252 pad(arr[5]) +2272 pad(arr[7])
2253 pad(arr[6]) +2273 );
2254 pad(arr[7])2274 } else {
2255 );2275 // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
2256 } else {2276 return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
2257 // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#21175232277 var r = (Math.random() * 16) | 0,
2258 return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {2278 v = c === 'x' ? r : (r & 0x3) | 0x8;
2259 var r = (Math.random() * 16) | 0,2279 return v.toString(16);
2260 v = c === 'x' ? r : (r & 0x3) | 0x8;2280 });
2261 return v.toString(16);2281 }
2262 });2282}
2263 }
2264 }
2265 2283
2266 /**2284/**
2267 * Given a child DOM element, returns a query-selector statement describing that2285 * Given a child DOM element, returns a query-selector statement describing that
2268 * and its ancestors2286 * and its ancestors
2269 * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]2287 * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
2270 * @param elem2288 * @param elem
2271 * @returns {string}2289 * @returns {string}
2272 */2290 */
2273 function htmlTreeAsString(elem) {2291function htmlTreeAsString(elem) {
2274 /* eslint no-extra-parens:0*/2292 /* eslint no-extra-parens:0*/
2275 var MAX_TRAVERSE_HEIGHT = 5,2293 var MAX_TRAVERSE_HEIGHT = 5,
2276 MAX_OUTPUT_LEN = 80,2294 MAX_OUTPUT_LEN = 80,
2277 out = [],2295 out = [],
2278 height = 0,2296 height = 0,
2279 len = 0,2297 len = 0,
2280 separator = ' > ',2298 separator = ' > ',
2281 sepLength = separator.length,2299 sepLength = separator.length,
2282 nextStr;2300 nextStr;
2283 2301
2284 while (elem && height++ < MAX_TRAVERSE_HEIGHT) {2302 while (elem && height++ < MAX_TRAVERSE_HEIGHT) {
2285 nextStr = htmlElementAsString(elem);2303 nextStr = htmlElementAsString(elem);
2286 // bail out if2304 // bail out if
2287 // - nextStr is the 'html' element2305 // - nextStr is the 'html' element
2288 // - the length of the string that would be created exceeds MAX_OUTPUT_LEN2306 // - the length of the string that would be created exceeds MAX_OUTPUT_LEN
2289 // (ignore this limit if we are on the first iteration)2307 // (ignore this limit if we are on the first iteration)
2290 if (2308 if (
2291 nextStr === 'html' ||2309 nextStr === 'html' ||
2292 (height > 1 &&2310 (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)
2293 len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)2311 ) {
2294 ) {2312 break;
2295 break;2313 }
2296 }
2297 2314
2298 out.push(nextStr);2315 out.push(nextStr);
2299 2316
2300 len += nextStr.length;2317 len += nextStr.length;
2301 elem = elem.parentNode;2318 elem = elem.parentNode;
2302 }2319 }
2303 2320
2304 return out.reverse().join(separator);2321 return out.reverse().join(separator);
2305 }2322}
2306 2323
2307 /**2324/**
2308 * Returns a simple, query-selector representation of a DOM element2325 * Returns a simple, query-selector representation of a DOM element
2309 * e.g. [HTMLElement] => input#foo.btn[name=baz]2326 * e.g. [HTMLElement] => input#foo.btn[name=baz]
2310 * @param HTMLElement2327 * @param HTMLElement
2311 * @returns {string}2328 * @returns {string}
2312 */2329 */
2313 function htmlElementAsString(elem) {2330function htmlElementAsString(elem) {
2314 var out = [],2331 var out = [],
2315 className,2332 className,
2316 classes,2333 classes,
2317 key,2334 key,
2318 attr,2335 attr,
2319 i;2336 i;
2320 2337
2321 if (!elem || !elem.tagName) {2338 if (!elem || !elem.tagName) {
2322 return '';2339 return '';
2323 }2340 }
2324 2341
2325 out.push(elem.tagName.toLowerCase());2342 out.push(elem.tagName.toLowerCase());
2326 if (elem.id) {2343 if (elem.id) {
2327 out.push('#' + elem.id);2344 out.push('#' + elem.id);
2328 }2345 }
2329 2346
2330 className = elem.className;2347 className = elem.className;
2331 if (className && isString(className)) {2348 if (className && isString(className)) {
2332 classes = className.split(/\s+/);2349 classes = className.split(/\s+/);
2333 for (i = 0; i < classes.length; i++) {2350 for (i = 0; i < classes.length; i++) {
2334 out.push('.' + classes[i]);2351 out.push('.' + classes[i]);
2335 }2352 }
2336 }2353 }
2337 var attrWhitelist = ['type', 'name', 'title', 'alt'];2354 var attrWhitelist = ['type', 'name', 'title', 'alt'];
2338 for (i = 0; i < attrWhitelist.length; i++) {2355 for (i = 0; i < attrWhitelist.length; i++) {
2339 key = attrWhitelist[i];2356 key = attrWhitelist[i];
2340 attr = elem.getAttribute(key);2357 attr = elem.getAttribute(key);
2341 if (attr) {2358 if (attr) {
2342 out.push('[' + key + '="' + attr + '"]');2359 out.push('[' + key + '="' + attr + '"]');
2343 }2360 }
2344 }2361 }
2345 return out.join('');2362 return out.join('');
2346 }2363}
2347 2364
2348 /**2365/**
2349 * Returns true if either a OR b is truthy, but not both2366 * Returns true if either a OR b is truthy, but not both
2350 */2367 */
2351 function isOnlyOneTruthy(a, b) {2368function isOnlyOneTruthy(a, b) {
2352 return !!(!!a ^ !!b);2369 return !!(!!a ^ !!b);
2353 }2370}
2354 2371
2355 /**2372/**
2356 * Returns true if the two input exception interfaces have the same content2373 * Returns true if the two input exception interfaces have the same content
2357 */2374 */
2358 function isSameException(ex1, ex2) {2375function isSameException(ex1, ex2) {
2359 if (isOnlyOneTruthy(ex1, ex2)) return false;2376 if (isOnlyOneTruthy(ex1, ex2)) return false;
2360 2377
2361 ex1 = ex1.values[0];2378 ex1 = ex1.values[0];
2362 ex2 = ex2.values[0];2379 ex2 = ex2.values[0];
2363 2380
2364 if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;2381 if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;
2365 2382
2366 return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);2383 return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
2367 }2384}
2368 2385
2369 /**2386/**
2370 * Returns true if the two input stack trace interfaces have the same content2387 * Returns true if the two input stack trace interfaces have the same content
2371 */2388 */
2372 function isSameStacktrace(stack1, stack2) {2389function isSameStacktrace(stack1, stack2) {
2373 if (isOnlyOneTruthy(stack1, stack2)) return false;2390 if (isOnlyOneTruthy(stack1, stack2)) return false;
2374 2391
2375 var frames1 = stack1.frames;2392 var frames1 = stack1.frames;
2376 var frames2 = stack2.frames;2393 var frames2 = stack2.frames;
2377 2394
2378 // Exit early if frame count differs2395 // Exit early if frame count differs
2379 if (frames1.length !== frames2.length) return false;2396 if (frames1.length !== frames2.length) return false;
2380 2397
2381 // Iterate through every frame; bail out if anything differs2398 // Iterate through every frame; bail out if anything differs
2382 var a, b;2399 var a, b;
2383 for (var i = 0; i < frames1.length; i++) {2400 for (var i = 0; i < frames1.length; i++) {
2384 a = frames1[i];2401 a = frames1[i];
2385 b = frames2[i];2402 b = frames2[i];
2386 if (2403 if (
2387 a.filename !== b.filename ||2404 a.filename !== b.filename ||
2388 a.lineno !== b.lineno ||2405 a.lineno !== b.lineno ||
2389 a.colno !== b.colno ||2406 a.colno !== b.colno ||
2390 a['function'] !== b['function']2407 a['function'] !== b['function']
2391 )2408 )
2392 return false;2409 return false;
2393 }2410 }
2394 return true;2411 return true;
2395 }2412}
2396 2413
2397 /**2414/**
2398 * Polyfill a method2415 * Polyfill a method
2399 * @param obj object e.g. `document`2416 * @param obj object e.g. `document`
2400 * @param name method name present on object e.g. `addEventListener`2417 * @param name method name present on object e.g. `addEventListener`
2401 * @param replacement replacement function2418 * @param replacement replacement function
2402 * @param track {optional} record instrumentation to an array2419 * @param track {optional} record instrumentation to an array
2403 */2420 */
2404 function fill(obj, name, replacement, track) {2421function fill(obj, name, replacement, track) {
2405 var orig = obj[name];2422 var orig = obj[name];
2406 obj[name] = replacement(orig);2423 obj[name] = replacement(orig);
2407 if (track) {2424 obj[name].__raven__ = true;
2408 track.push([obj, name, orig]);2425 obj[name].__orig__ = orig;
2409 }2426 if (track) {
2410 }2427 track.push([obj, name, orig]);
2411 2428 }
2412 if (typeof __DEV__ !== 'undefined' && __DEV__) {2429}
2413 Raven.utils = {2430
2414 isUndefined: isUndefined,2431module.exports = {
2415 isFunction: isFunction,2432 isObject: isObject,
2416 isString: isString,2433 isError: isError,
2417 isObject: isObject,2434 isErrorEvent: isErrorEvent,
2418 isEmptyObject: isEmptyObject,2435 isUndefined: isUndefined,
2419 isError: isError,2436 isFunction: isFunction,
2420 each: each,2437 isString: isString,
2421 objectMerge: objectMerge,2438 isArray: isArray,
2422 truncate: truncate,2439 isEmptyObject: isEmptyObject,
2423 hasKey: hasKey,2440 supportsErrorEvent: supportsErrorEvent,
2424 joinRegExp: joinRegExp,2441 wrappedCallback: wrappedCallback,
2425 urlencode: urlencode,2442 each: each,
2426 uuid4: uuid4,2443 objectMerge: objectMerge,
2427 htmlTreeAsString: htmlTreeAsString,2444 truncate: truncate,
2428 htmlElementAsString: htmlElementAsString,2445 objectFrozen: objectFrozen,
2429 parseUrl: parseUrl,2446 hasKey: hasKey,
2430 fill: fill2447 joinRegExp: joinRegExp,
2431 };2448 urlencode: urlencode,
2432 }2449 uuid4: uuid4,
2433 2450 htmlTreeAsString: htmlTreeAsString,
2434 // Deprecations2451 htmlElementAsString: htmlElementAsString,
2435 Raven.prototype.setUser = Raven.prototype.setUserContext;2452 isSameException: isSameException,
2436 Raven.prototype.setReleaseContext = Raven.prototype.setRelease;2453 isSameStacktrace: isSameStacktrace,
2437 2454 parseUrl: parseUrl,
2438 module.exports = Raven;2455 fill: fill
2439 }.call(2456};
2440 this,2457
2441 typeof global !== 'undefined'2458}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
2442 ? global2459},{}],6:[function(_dereq_,module,exports){
2443 : typeof self !== 'undefined'2460(function (global){
2444 ? self2461var utils = _dereq_(5);
2445 : typeof window !== 'undefined' ? window : {}
2446 ));
2447 },
2448 {'1': 1, '2': 2, '5': 5, '6': 6, '7': 7}
2449 ],
2450 4: [
2451 function(_dereq_, module, exports) {
2452 (function(global) {
2453 /**
2454 * Enforces a single instance of the Raven client, and the
2455 * main entry point for Raven. If you are a consumer of the
2456 * Raven library, you SHOULD load this file (vs raven.js).
2457 **/
2458
2459 var RavenConstructor = _dereq_(3);
2460
2461 // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
2462 var _window =
2463 typeof window !== 'undefined'
2464 ? window
2465 : typeof global !== 'undefined'
2466 ? global
2467 : typeof self !== 'undefined' ? self : {};
2468 var _Raven = _window.Raven;
2469
2470 var Raven = new RavenConstructor();
2471
2472 /*
2473 * Allow multiple versions of Raven to be installed.
2474 * Strip Raven from the global context and returns the instance.
2475 *
2476 * @return {Raven}
2477 */
2478 Raven.noConflict = function() {
2479 _window.Raven = _Raven;
2480 return Raven;
2481 };
2482
2483 Raven.afterLoad();
2484
2485 module.exports = Raven;
2486 }.call(
2487 this,
2488 typeof global !== 'undefined'
2489 ? global
2490 : typeof self !== 'undefined'
2491 ? self
2492 : typeof window !== 'undefined' ? window : {}
2493 ));
2494 },
2495 {'3': 3}
2496 ],
2497 5: [
2498 function(_dereq_, module, exports) {
2499 function isObject(what) {
2500 return typeof what === 'object' && what !== null;
2501 }
2502
2503 // Yanked from https://git.io/vS8DV re-used under CC0
2504 // with some tiny modifications
2505 function isError(value) {
2506 switch ({}.toString.call(value)) {
2507 case '[object Error]':
2508 return true;
2509 case '[object Exception]':
2510 return true;
2511 case '[object DOMException]':
2512 return true;
2513 default:
2514 return value instanceof Error;
2515 }
2516 }
2517
2518 function wrappedCallback(callback) {
2519 function dataCallback(data, original) {
2520 var normalizedData = callback(data) || data;
2521 if (original) {
2522 return original(normalizedData) || normalizedData;
2523 }
2524 return normalizedData;
2525 }
2526
2527 return dataCallback;
2528 }
2529 2462
2530 module.exports = {2463/*
2531 isObject: isObject,
2532 isError: isError,
2533 wrappedCallback: wrappedCallback
2534 };
2535 },
2536 {}
2537 ],
2538 6: [
2539 function(_dereq_, module, exports) {
2540 (function(global) {
2541 var utils = _dereq_(5);
2542
2543 /*
2544 TraceKit - Cross brower stack traces2464 TraceKit - Cross brower stack traces
2545 2465
2546 This was originally forked from github.com/occ/TraceKit, but has since been2466 This was originally forked from github.com/occ/TraceKit, but has since been
@@ -2550,33 +2470,31 @@ · 3 unmodified lines
2550 MIT license2470 MIT license
2551*/2471*/
2552 2472
2553 var TraceKit = {2473var TraceKit = {
2554 collectWindowErrors: true,2474 collectWindowErrors: true,
2555 debug: false2475 debug: false
2556 };2476};
2557 2477
2558 // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)2478// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
2559 var _window =2479var _window =
2560 typeof window !== 'undefined'2480 typeof window !== 'undefined'
2561 ? window2481 ? window
2562 : typeof global !== 'undefined'2482 : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
2563 ? global
2564 : typeof self !== 'undefined' ? self : {};
2565 2483
2566 // global reference to slice2484// global reference to slice
2567 var _slice = [].slice;2485var _slice = [].slice;
2568 var UNKNOWN_FUNCTION = '?';2486var UNKNOWN_FUNCTION = '?';
2569 2487
2570 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types2488// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
2571 var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/;2489var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/;
2572 2490
2573 function getLocationHref() {2491function getLocationHref() {
2574 if (typeof document === 'undefined' || document.location == null) return '';2492 if (typeof document === 'undefined' || document.location == null) return '';
2575 2493
2576 return document.location.href;2494 return document.location.href;
2577 }2495}
2578 2496
2579 /**2497/**
2580 * TraceKit.report: cross-browser processing of unhandled exceptions2498 * TraceKit.report: cross-browser processing of unhandled exceptions
2581 *2499 *
2582 * Syntax:2500 * Syntax:
@@ -2615,68 +2533,68 @@ · 32 unmodified lines
2615 * Handlers receive a stackInfo object as described in the2533 * Handlers receive a stackInfo object as described in the
2616 * TraceKit.computeStackTrace docs.2534 * TraceKit.computeStackTrace docs.
2617 */2535 */
2618 TraceKit.report = (function reportModuleWrapper() {2536TraceKit.report = (function reportModuleWrapper() {
2619 var handlers = [],2537 var handlers = [],
2620 lastArgs = null,2538 lastArgs = null,
2621 lastException = null,2539 lastException = null,
2622 lastExceptionStack = null;2540 lastExceptionStack = null;
2623 2541
2624 /**2542 /**
2625 * Add a crash handler.2543 * Add a crash handler.
2626 * @param {Function} handler2544 * @param {Function} handler
2627 */2545 */
2628 function subscribe(handler) {2546 function subscribe(handler) {
2629 installGlobalHandler();2547 installGlobalHandler();
2630 handlers.push(handler);2548 handlers.push(handler);
2631 }2549 }
2632 2550
2633 /**2551 /**
2634 * Remove a crash handler.2552 * Remove a crash handler.
2635 * @param {Function} handler2553 * @param {Function} handler
2636 */2554 */
2637 function unsubscribe(handler) {2555 function unsubscribe(handler) {
2638 for (var i = handlers.length - 1; i >= 0; --i) {2556 for (var i = handlers.length - 1; i >= 0; --i) {
2639 if (handlers[i] === handler) {2557 if (handlers[i] === handler) {
2640 handlers.splice(i, 1);2558 handlers.splice(i, 1);
2641 }2559 }
2642 }2560 }
2643 }2561 }
2644 2562
2645 /**2563 /**
2646 * Remove all crash handlers.2564 * Remove all crash handlers.
2647 */2565 */
2648 function unsubscribeAll() {2566 function unsubscribeAll() {
2649 uninstallGlobalHandler();2567 uninstallGlobalHandler();
2650 handlers = [];2568 handlers = [];
2651 }2569 }
2652 2570
2653 /**2571 /**
2654 * Dispatch stack information to all handlers.2572 * Dispatch stack information to all handlers.
2655 * @param {Object.<string, *>} stack2573 * @param {Object.<string, *>} stack
2656 */2574 */
2657 function notifyHandlers(stack, isWindowError) {2575 function notifyHandlers(stack, isWindowError) {
2658 var exception = null;2576 var exception = null;
2659 if (isWindowError && !TraceKit.collectWindowErrors) {2577 if (isWindowError && !TraceKit.collectWindowErrors) {
2660 return;2578 return;
2661 }2579 }
2662 for (var i in handlers) {2580 for (var i in handlers) {
2663 if (handlers.hasOwnProperty(i)) {2581 if (handlers.hasOwnProperty(i)) {
2664 try {2582 try {
2665 handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));2583 handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
2666 } catch (inner) {2584 } catch (inner) {
2667 exception = inner;2585 exception = inner;
2668 }2586 }
2669 }2587 }
2670 }2588 }
2671 2589
2672 if (exception) {2590 if (exception) {
2673 throw exception;2591 throw exception;
2674 }2592 }
2675 }2593 }
2676 2594
2677 var _oldOnerrorHandler, _onErrorHandlerInstalled;2595 var _oldOnerrorHandler, _onErrorHandlerInstalled;
2678 2596
2679 /**2597 /**
2680 * Ensures all global unhandled exceptions are recorded.2598 * Ensures all global unhandled exceptions are recorded.
2681 * Supported by Gecko and IE.2599 * Supported by Gecko and IE.
2682 * @param {string} message Error message.2600 * @param {string} message Error message.
@@ -2687,135 +2605,132 @@ · 4 unmodified lines
2687 * occurred.2605 * occurred.
2688 * @param {?Error} ex The actual Error object.2606 * @param {?Error} ex The actual Error object.
2689 */2607 */
2690 function traceKitWindowOnError(message, url, lineNo, colNo, ex) {2608 function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
2691 var stack = null;2609 var stack = null;
2692 2610
2693 if (lastExceptionStack) {2611 if (lastExceptionStack) {
2694 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(2612 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(
2695 lastExceptionStack,2613 lastExceptionStack,
2696 url,2614 url,
2697 lineNo,2615 lineNo,
2698 message2616 message
2699 );2617 );
2700 processLastException();2618 processLastException();
2701 } else if (ex && utils.isError(ex)) {2619 } else if (ex && utils.isError(ex)) {
2702 // non-string `ex` arg; attempt to extract stack trace2620 // non-string `ex` arg; attempt to extract stack trace
2703 2621
2704 // New chrome and blink send along a real error object2622 // New chrome and blink send along a real error object
2705 // Let's just report that like a normal error.2623 // Let's just report that like a normal error.
2706 // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror2624 // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
2707 stack = TraceKit.computeStackTrace(ex);2625 stack = TraceKit.computeStackTrace(ex);
2708 notifyHandlers(stack, true);2626 notifyHandlers(stack, true);
2709 } else {2627 } else {
2710 var location = {2628 var location = {
2711 url: url,2629 url: url,
2712 line: lineNo,2630 line: lineNo,
2713 column: colNo2631 column: colNo
2714 };2632 };
2715 2633
2716 var name = undefined;2634 var name = undefined;
2717 var msg = message; // must be new var or will modify original `arguments`2635 var msg = message; // must be new var or will modify original `arguments`
2718 var groups;2636 var groups;
2719 if ({}.toString.call(message) === '[object String]') {2637 if ({}.toString.call(message) === '[object String]') {
2720 var groups = message.match(ERROR_TYPES_RE);2638 var groups = message.match(ERROR_TYPES_RE);
2721 if (groups) {2639 if (groups) {
2722 name = groups[1];2640 name = groups[1];
2723 msg = groups[2];2641 msg = groups[2];
2724 }2642 }
2725 }2643 }
2726 2644
2727 location.func = UNKNOWN_FUNCTION;2645 location.func = UNKNOWN_FUNCTION;
2728 2646
2729 stack = {2647 stack = {
2730 name: name,2648 name: name,
2731 message: msg,2649 message: msg,
2732 url: getLocationHref(),2650 url: getLocationHref(),
2733 stack: [location]2651 stack: [location]
2734 };2652 };
2735 notifyHandlers(stack, true);2653 notifyHandlers(stack, true);
2736 }2654 }
2737 2655
2738 if (_oldOnerrorHandler) {2656 if (_oldOnerrorHandler) {
2739 return _oldOnerrorHandler.apply(this, arguments);2657 return _oldOnerrorHandler.apply(this, arguments);
2740 }2658 }
2741 2659
2742 return false;2660 return false;
2743 }2661 }
2744 2662
2745 function installGlobalHandler() {2663 function installGlobalHandler() {
2746 if (_onErrorHandlerInstalled) {2664 if (_onErrorHandlerInstalled) {
2747 return;2665 return;
2748 }2666 }
2749 _oldOnerrorHandler = _window.onerror;2667 _oldOnerrorHandler = _window.onerror;
2750 _window.onerror = traceKitWindowOnError;2668 _window.onerror = traceKitWindowOnError;
2751 _onErrorHandlerInstalled = true;2669 _onErrorHandlerInstalled = true;
2752 }2670 }
2753 2671
2754 function uninstallGlobalHandler() {2672 function uninstallGlobalHandler() {
2755 if (!_onErrorHandlerInstalled) {2673 if (!_onErrorHandlerInstalled) {
2756 return;2674 return;
2757 }2675 }
2758 _window.onerror = _oldOnerrorHandler;2676 _window.onerror = _oldOnerrorHandler;
2759 _onErrorHandlerInstalled = false;2677 _onErrorHandlerInstalled = false;
2760 _oldOnerrorHandler = undefined;2678 _oldOnerrorHandler = undefined;
2761 }2679 }
2762 2680
2763 function processLastException() {2681 function processLastException() {
2764 var _lastExceptionStack = lastExceptionStack,2682 var _lastExceptionStack = lastExceptionStack,
2765 _lastArgs = lastArgs;2683 _lastArgs = lastArgs;
2766 lastArgs = null;2684 lastArgs = null;
2767 lastExceptionStack = null;2685 lastExceptionStack = null;
2768 lastException = null;2686 lastException = null;
2769 notifyHandlers.apply(2687 notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));
2770 null,2688 }
2771 [_lastExceptionStack, false].concat(_lastArgs)
2772 );
2773 }
2774 2689
2775 /**2690 /**
2776 * Reports an unhandled Error to TraceKit.2691 * Reports an unhandled Error to TraceKit.
2777 * @param {Error} ex2692 * @param {Error} ex
2778 * @param {?boolean} rethrow If false, do not re-throw the exception.2693 * @param {?boolean} rethrow If false, do not re-throw the exception.
2779 * Only used for window.onerror to not cause an infinite loop of2694 * Only used for window.onerror to not cause an infinite loop of
2780 * rethrowing.2695 * rethrowing.
2781 */2696 */
2782 function report(ex, rethrow) {2697 function report(ex, rethrow) {
2783 var args = _slice.call(arguments, 1);2698 var args = _slice.call(arguments, 1);
2784 if (lastExceptionStack) {2699 if (lastExceptionStack) {
2785 if (lastException === ex) {2700 if (lastException === ex) {
2786 return; // already caught by an inner catch block, ignore2701 return; // already caught by an inner catch block, ignore
2787 } else {2702 } else {
2788 processLastException();2703 processLastException();
2789 }2704 }
2790 }2705 }
2791 2706
2792 var stack = TraceKit.computeStackTrace(ex);2707 var stack = TraceKit.computeStackTrace(ex);
2793 lastExceptionStack = stack;2708 lastExceptionStack = stack;
2794 lastException = ex;2709 lastException = ex;
2795 lastArgs = args;2710 lastArgs = args;
2796 2711
2797 // If the stack trace is incomplete, wait for 2 seconds for2712 // If the stack trace is incomplete, wait for 2 seconds for
2798 // slow slow IE to see if onerror occurs or not before reporting2713 // slow slow IE to see if onerror occurs or not before reporting
2799 // this exception; otherwise, we will end up with an incomplete2714 // this exception; otherwise, we will end up with an incomplete
2800 // stack trace2715 // stack trace
2801 setTimeout(function() {2716 setTimeout(function() {
2802 if (lastException === ex) {2717 if (lastException === ex) {
2803 processLastException();2718 processLastException();
2804 }2719 }
2805 }, stack.incomplete ? 2000 : 0);2720 }, stack.incomplete ? 2000 : 0);
2806 2721
2807 if (rethrow !== false) {2722 if (rethrow !== false) {
2808 throw ex; // re-throw to propagate to the top level (and cause window.onerror)2723 throw ex; // re-throw to propagate to the top level (and cause window.onerror)
2809 }2724 }
2810 }2725 }
2811 2726
2812 report.subscribe = subscribe;
2813 report.unsubscribe = unsubscribe;
2814 report.uninstall = unsubscribeAll;
2815 return report;
2816 })();

724 more lines aren’t shown here.