diff --git a/assets/javascripts/vendor/raven.js b/assets/javascripts/vendor/raven.js index 591b6767..f36c41b2 100644 --- a/assets/javascripts/vendor/raven.js +++ b/assets/javascripts/vendor/raven.js @@ -1,4 +1,4 @@ -/*! Raven.js 3.18.0 (b771bb2) | github.com/getsentry/raven-js */ +/*! Raven.js 3.20.1 (42adaf5) | github.com/getsentry/raven-js */ /* * Includes TraceKit @@ -10,322 +10,270 @@ * */ -(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) { - function RavenConfigError(message) { - this.name = 'RavenConfigError'; - this.message = message; - } - RavenConfigError.prototype = new Error(); - RavenConfigError.prototype.constructor = RavenConfigError; - - module.exports = RavenConfigError; - }, - {} - ], - 2: [ - function(_dereq_, module, exports) { - var wrapMethod = function(console, level, callback) { - var originalConsoleLevel = console[level]; - var originalConsole = console; - - if (!(level in console)) { - return; - } +(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 this._globalOptions.maxBreadcrumbs) { - this._breadcrumbs.shift(); - } - return this; - }, + if (isFunction(this._globalOptions.breadcrumbCallback)) { + var result = this._globalOptions.breadcrumbCallback(crumb); + + if (isObject(result) && !isEmptyObject(result)) { + crumb = result; + } else if (result === false) { + return this; + } + } - addPlugin: function(plugin /*arg1, arg2, ... argN*/) { - var pluginArgs = [].slice.call(arguments, 1); + this._breadcrumbs.push(crumb); + if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) { + this._breadcrumbs.shift(); + } + return this; + }, - this._plugins.push([plugin, pluginArgs]); - if (this._isRavenInstalled) { - this._drainPlugins(); - } + addPlugin: function(plugin /*arg1, arg2, ... argN*/) { + var pluginArgs = [].slice.call(arguments, 1); - return this; - }, + this._plugins.push([plugin, pluginArgs]); + if (this._isRavenInstalled) { + this._drainPlugins(); + } + + return this; + }, - /* + /* * Set/clear a user to be sent along with the payload. * * @param {object} user An object representing user data [optional] * @return {Raven} */ - setUserContext: function(user) { - // Intentionally do not merge here since that's an unexpected behavior. - this._globalContext.user = user; + setUserContext: function(user) { + // Intentionally do not merge here since that's an unexpected behavior. + this._globalContext.user = user; - return this; - }, + return this; + }, - /* + /* * Merge extra attributes to be sent along with the payload. * * @param {object} extra An object representing extra data [optional] * @return {Raven} */ - setExtraContext: function(extra) { - this._mergeContext('extra', extra); + setExtraContext: function(extra) { + this._mergeContext('extra', extra); - return this; - }, + return this; + }, - /* + /* * Merge tags to be sent along with the payload. * * @param {object} tags An object representing tags [optional] * @return {Raven} */ - setTagsContext: function(tags) { - this._mergeContext('tags', tags); + setTagsContext: function(tags) { + this._mergeContext('tags', tags); - return this; - }, + return this; + }, - /* + /* * Clear all of the context. * * @return {Raven} */ - clearContext: function() { - this._globalContext = {}; + clearContext: function() { + this._globalContext = {}; - return this; - }, + return this; + }, - /* + /* * Get a copy of the current context. This cannot be mutated. * * @return {object} copy of context */ - getContext: function() { - // lol javascript - return JSON.parse(stringify(this._globalContext)); - }, + getContext: function() { + // lol javascript + return JSON.parse(stringify(this._globalContext)); + }, - /* + /* * Set environment of application * * @param {string} environment Typically something like 'production'. * @return {Raven} */ - setEnvironment: function(environment) { - this._globalOptions.environment = environment; + setEnvironment: function(environment) { + this._globalOptions.environment = environment; - return this; - }, + return this; + }, - /* + /* * Set release version of application * * @param {string} release Typically something like a git SHA to identify version * @return {Raven} */ - setRelease: function(release) { - this._globalOptions.release = release; + setRelease: function(release) { + this._globalOptions.release = release; - return this; - }, + return this; + }, - /* + /* * Set the dataCallback option * * @param {function} callback The callback to run which allows the * data blob to be mutated before sending * @return {Raven} */ - setDataCallback: function(callback) { - var original = this._globalOptions.dataCallback; - this._globalOptions.dataCallback = keepOriginalCallback( - original, - callback - ); - return this; - }, - - /* + setDataCallback: function(callback) { + var original = this._globalOptions.dataCallback; + this._globalOptions.dataCallback = keepOriginalCallback(original, callback); + return this; + }, + + /* * Set the breadcrumbCallback option * * @param {function} callback The callback to run which allows filtering * or mutating breadcrumbs * @return {Raven} */ - setBreadcrumbCallback: function(callback) { - var original = this._globalOptions.breadcrumbCallback; - this._globalOptions.breadcrumbCallback = keepOriginalCallback( - original, - callback - ); - return this; - }, - - /* + setBreadcrumbCallback: function(callback) { + var original = this._globalOptions.breadcrumbCallback; + this._globalOptions.breadcrumbCallback = keepOriginalCallback(original, callback); + return this; + }, + + /* * Set the shouldSendCallback option * * @param {function} callback The callback to run which allows * introspecting the blob before sending * @return {Raven} */ - setShouldSendCallback: function(callback) { - var original = this._globalOptions.shouldSendCallback; - this._globalOptions.shouldSendCallback = keepOriginalCallback( - original, - callback - ); - return this; - }, - - /** + setShouldSendCallback: function(callback) { + var original = this._globalOptions.shouldSendCallback; + this._globalOptions.shouldSendCallback = keepOriginalCallback(original, callback); + return this; + }, + + /** * Override the default HTTP transport mechanism that transmits data * to the Sentry server. * @@ -798,438 +757,443 @@ * * @return {Raven} */ - setTransport: function(transport) { - this._globalOptions.transport = transport; + setTransport: function(transport) { + this._globalOptions.transport = transport; - return this; - }, + return this; + }, - /* + /* * Get the latest raw exception that was captured by Raven. * * @return {error} */ - lastException: function() { - return this._lastCapturedException; - }, + lastException: function() { + return this._lastCapturedException; + }, - /* + /* * Get the last event id * * @return {string} */ - lastEventId: function() { - return this._lastEventId; - }, + lastEventId: function() { + return this._lastEventId; + }, - /* + /* * Determine if Raven is setup and ready to go. * * @return {boolean} */ - isSetup: function() { - if (!this._hasJSON) return false; // needs JSON support - if (!this._globalServer) { - if (!this.ravenNotConfiguredError) { - this.ravenNotConfiguredError = true; - this._logDebug('error', 'Error: Raven has not been configured.'); - } - return false; - } - return true; - }, + isSetup: function() { + if (!this._hasJSON) return false; // needs JSON support + if (!this._globalServer) { + if (!this.ravenNotConfiguredError) { + this.ravenNotConfiguredError = true; + this._logDebug('error', 'Error: Raven has not been configured.'); + } + return false; + } + return true; + }, - afterLoad: function() { - // TODO: remove window dependence? + afterLoad: function() { + // TODO: remove window dependence? - // Attempt to initialize Raven on load - var RavenConfig = _window.RavenConfig; - if (RavenConfig) { - this.config(RavenConfig.dsn, RavenConfig.config).install(); - } - }, + // Attempt to initialize Raven on load + var RavenConfig = _window.RavenConfig; + if (RavenConfig) { + this.config(RavenConfig.dsn, RavenConfig.config).install(); + } + }, - showReportDialog: function(options) { - if ( - !_document // doesn't work without a document (React native) - ) - return; + showReportDialog: function(options) { + if ( + !_document // doesn't work without a document (React native) + ) + return; - options = options || {}; + options = options || {}; - var lastEventId = options.eventId || this.lastEventId(); - if (!lastEventId) { - throw new RavenConfigError('Missing eventId'); - } + var lastEventId = options.eventId || this.lastEventId(); + if (!lastEventId) { + throw new RavenConfigError('Missing eventId'); + } - var dsn = options.dsn || this._dsn; - if (!dsn) { - throw new RavenConfigError('Missing DSN'); - } + var dsn = options.dsn || this._dsn; + if (!dsn) { + throw new RavenConfigError('Missing DSN'); + } - var encode = encodeURIComponent; - var qs = ''; - qs += '?eventId=' + encode(lastEventId); - qs += '&dsn=' + encode(dsn); + var encode = encodeURIComponent; + var qs = ''; + qs += '?eventId=' + encode(lastEventId); + qs += '&dsn=' + encode(dsn); - var user = options.user || this._globalContext.user; - if (user) { - if (user.name) qs += '&name=' + encode(user.name); - if (user.email) qs += '&email=' + encode(user.email); - } + var user = options.user || this._globalContext.user; + if (user) { + if (user.name) qs += '&name=' + encode(user.name); + if (user.email) qs += '&email=' + encode(user.email); + } - var globalServer = this._getGlobalServer(this._parseDSN(dsn)); - - var script = _document.createElement('script'); - script.async = true; - script.src = globalServer + '/api/embed/error-page/' + qs; - (_document.head || _document.body).appendChild(script); - }, - - /**** Private functions ****/ - _ignoreNextOnError: function() { - var self = this; - this._ignoreOnError += 1; - setTimeout(function() { - // onerror should trigger before setTimeout - self._ignoreOnError -= 1; - }); - }, + var globalServer = this._getGlobalServer(this._parseDSN(dsn)); - _triggerEvent: function(eventType, options) { - // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it - var evt, key; + var script = _document.createElement('script'); + script.async = true; + script.src = globalServer + '/api/embed/error-page/' + qs; + (_document.head || _document.body).appendChild(script); + }, - if (!this._hasDocument) return; + /**** Private functions ****/ + _ignoreNextOnError: function() { + var self = this; + this._ignoreOnError += 1; + setTimeout(function() { + // onerror should trigger before setTimeout + self._ignoreOnError -= 1; + }); + }, - options = options || {}; + _triggerEvent: function(eventType, options) { + // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it + var evt, key; - eventType = - 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1); + if (!this._hasDocument) return; - if (_document.createEvent) { - evt = _document.createEvent('HTMLEvents'); - evt.initEvent(eventType, true, true); - } else { - evt = _document.createEventObject(); - evt.eventType = eventType; - } + options = options || {}; - for (key in options) - if (hasKey(options, key)) { - evt[key] = options[key]; - } + eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1); - if (_document.createEvent) { - // IE9 if standards - _document.dispatchEvent(evt); - } else { - // IE8 regardless of Quirks or Standards - // IE9 if quirks - try { - _document.fireEvent('on' + evt.eventType.toLowerCase(), evt); - } catch (e) { - // Do nothing - } - } - }, + if (_document.createEvent) { + evt = _document.createEvent('HTMLEvents'); + evt.initEvent(eventType, true, true); + } else { + evt = _document.createEventObject(); + evt.eventType = eventType; + } + + for (key in options) + if (hasKey(options, key)) { + evt[key] = options[key]; + } + + if (_document.createEvent) { + // IE9 if standards + _document.dispatchEvent(evt); + } else { + // IE8 regardless of Quirks or Standards + // IE9 if quirks + try { + _document.fireEvent('on' + evt.eventType.toLowerCase(), evt); + } catch (e) { + // Do nothing + } + } + }, - /** + /** * Wraps addEventListener to capture UI breadcrumbs * @param evtName the event name (e.g. "click") * @returns {Function} * @private */ - _breadcrumbEventHandler: function(evtName) { - var self = this; - return function(evt) { - // reset keypress timeout; e.g. triggering a 'click' after - // a 'keypress' will reset the keypress debounce so that a new - // set of keypresses can be recorded - self._keypressTimeout = null; - - // It's possible this handler might trigger multiple times for the same - // event (e.g. event propagation through node ancestors). Ignore if we've - // already captured the event. - if (self._lastCapturedEvent === evt) return; - - self._lastCapturedEvent = evt; - - // try/catch both: - // - accessing evt.target (see getsentry/raven-js#838, #768) - // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly - // can throw an exception in some circumstances. - var target; - try { - target = htmlTreeAsString(evt.target); - } catch (e) { - target = ''; - } + _breadcrumbEventHandler: function(evtName) { + var self = this; + return function(evt) { + // reset keypress timeout; e.g. triggering a 'click' after + // a 'keypress' will reset the keypress debounce so that a new + // set of keypresses can be recorded + self._keypressTimeout = null; + + // It's possible this handler might trigger multiple times for the same + // event (e.g. event propagation through node ancestors). Ignore if we've + // already captured the event. + if (self._lastCapturedEvent === evt) return; + + self._lastCapturedEvent = evt; + + // try/catch both: + // - accessing evt.target (see getsentry/raven-js#838, #768) + // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly + // can throw an exception in some circumstances. + var target; + try { + target = htmlTreeAsString(evt.target); + } catch (e) { + target = ''; + } - self.captureBreadcrumb({ - category: 'ui.' + evtName, // e.g. ui.click, ui.input - message: target - }); - }; - }, + self.captureBreadcrumb({ + category: 'ui.' + evtName, // e.g. ui.click, ui.input + message: target + }); + }; + }, - /** + /** * Wraps addEventListener to capture keypress UI events * @returns {Function} * @private */ - _keypressEventHandler: function() { - var self = this, - debounceDuration = 1000; // milliseconds - - // TODO: if somehow user switches keypress target before - // debounce timeout is triggered, we will only capture - // a single breadcrumb from the FIRST target (acceptable?) - return function(evt) { - var target; - try { - target = evt.target; - } catch (e) { - // just accessing event properties can throw an exception in some rare circumstances - // see: https://github.com/getsentry/raven-js/issues/838 - return; - } - var tagName = target && target.tagName; - - // only consider keypress events on actual input elements - // this will disregard keypresses targeting body (e.g. tabbing - // through elements, hotkeys, etc) - if ( - !tagName || - (tagName !== 'INPUT' && - tagName !== 'TEXTAREA' && - !target.isContentEditable) - ) - return; - - // record first keypress in a series, but ignore subsequent - // keypresses until debounce clears - var timeout = self._keypressTimeout; - if (!timeout) { - self._breadcrumbEventHandler('input')(evt); - } - clearTimeout(timeout); - self._keypressTimeout = setTimeout(function() { - self._keypressTimeout = null; - }, debounceDuration); - }; - }, - - /** + _keypressEventHandler: function() { + var self = this, + debounceDuration = 1000; // milliseconds + + // TODO: if somehow user switches keypress target before + // debounce timeout is triggered, we will only capture + // a single breadcrumb from the FIRST target (acceptable?) + return function(evt) { + var target; + try { + target = evt.target; + } catch (e) { + // just accessing event properties can throw an exception in some rare circumstances + // see: https://github.com/getsentry/raven-js/issues/838 + return; + } + var tagName = target && target.tagName; + + // only consider keypress events on actual input elements + // this will disregard keypresses targeting body (e.g. tabbing + // through elements, hotkeys, etc) + if ( + !tagName || + (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !target.isContentEditable) + ) + return; + + // record first keypress in a series, but ignore subsequent + // keypresses until debounce clears + var timeout = self._keypressTimeout; + if (!timeout) { + self._breadcrumbEventHandler('input')(evt); + } + clearTimeout(timeout); + self._keypressTimeout = setTimeout(function() { + self._keypressTimeout = null; + }, debounceDuration); + }; + }, + + /** * Captures a breadcrumb of type "navigation", normalizing input URLs * @param to the originating URL * @param from the target URL * @private */ - _captureUrlChange: function(from, to) { - var parsedLoc = parseUrl(this._location.href); - var parsedTo = parseUrl(to); - var parsedFrom = parseUrl(from); - - // because onpopstate only tells you the "new" (to) value of location.href, and - // not the previous (from) value, we need to track the value of the current URL - // state ourselves - this._lastHref = to; - - // Use only the path component of the URL if the URL matches the current - // document (almost all the time when using pushState) - if ( - parsedLoc.protocol === parsedTo.protocol && - parsedLoc.host === parsedTo.host - ) - to = parsedTo.relative; - if ( - parsedLoc.protocol === parsedFrom.protocol && - parsedLoc.host === parsedFrom.host - ) - from = parsedFrom.relative; - - this.captureBreadcrumb({ - category: 'navigation', - data: { - to: to, - from: from - } - }); - }, + _captureUrlChange: function(from, to) { + var parsedLoc = parseUrl(this._location.href); + var parsedTo = parseUrl(to); + var parsedFrom = parseUrl(from); + + // because onpopstate only tells you the "new" (to) value of location.href, and + // not the previous (from) value, we need to track the value of the current URL + // state ourselves + this._lastHref = to; + + // Use only the path component of the URL if the URL matches the current + // document (almost all the time when using pushState) + if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) + to = parsedTo.relative; + if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) + from = parsedFrom.relative; + + this.captureBreadcrumb({ + category: 'navigation', + data: { + to: to, + from: from + } + }); + }, + + _patchFunctionToString: function() { + var self = this; + self._originalFunctionToString = Function.prototype.toString; + // eslint-disable-next-line no-extend-native + Function.prototype.toString = function() { + if (typeof this === 'function' && this.__raven__) { + return self._originalFunctionToString.apply(this.__orig__, arguments); + } + return self._originalFunctionToString.apply(this, arguments); + }; + }, + + _unpatchFunctionToString: function() { + if (this._originalFunctionToString) { + // eslint-disable-next-line no-extend-native + Function.prototype.toString = this._originalFunctionToString; + } + }, - /** + /** * Wrap timer functions and event targets to catch errors and provide * better metadata. */ - _instrumentTryCatch: function() { - var self = this; - - var wrappedBuiltIns = self._wrappedBuiltIns; - - function wrapTimeFn(orig) { - return function(fn, t) { - // preserve arity - // Make a copy of the arguments to prevent deoptimization - // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments - var args = new Array(arguments.length); - for (var i = 0; i < args.length; ++i) { - args[i] = arguments[i]; - } - var originalCallback = args[0]; - if (isFunction(originalCallback)) { - args[0] = self.wrap(originalCallback); - } - - // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it - // also supports only two arguments and doesn't care what this is, so we - // can just call the original function directly. - if (orig.apply) { - return orig.apply(this, args); - } else { - return orig(args[0], args[1]); - } - }; - } + _instrumentTryCatch: function() { + var self = this; + + var wrappedBuiltIns = self._wrappedBuiltIns; + + function wrapTimeFn(orig) { + return function(fn, t) { + // preserve arity + // Make a copy of the arguments to prevent deoptimization + // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var originalCallback = args[0]; + if (isFunction(originalCallback)) { + args[0] = self.wrap(originalCallback); + } + + // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it + // also supports only two arguments and doesn't care what this is, so we + // can just call the original function directly. + if (orig.apply) { + return orig.apply(this, args); + } else { + return orig(args[0], args[1]); + } + }; + } + + var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; + + function wrapEventTarget(global) { + var proto = _window[global] && _window[global].prototype; + if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) { + fill( + proto, + 'addEventListener', + function(orig) { + return function(evtName, fn, capture, secure) { + // preserve arity + try { + if (fn && fn.handleEvent) { + fn.handleEvent = self.wrap(fn.handleEvent); + } + } catch (err) { + // can sometimes get 'Permission denied to access property "handle Event' + } - var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; - - function wrapEventTarget(global) { - var proto = _window[global] && _window[global].prototype; - if ( - proto && - proto.hasOwnProperty && - proto.hasOwnProperty('addEventListener') - ) { - fill( - proto, - 'addEventListener', - function(orig) { - return function(evtName, fn, capture, secure) { - // preserve arity - try { - if (fn && fn.handleEvent) { - fn.handleEvent = self.wrap(fn.handleEvent); - } - } catch (err) { - // can sometimes get 'Permission denied to access property "handle Event' - } - - // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` - // so that we don't have more than one wrapper function - var before, clickHandler, keypressHandler; - - if ( - autoBreadcrumbs && - autoBreadcrumbs.dom && - (global === 'EventTarget' || global === 'Node') - ) { - // NOTE: generating multiple handlers per addEventListener invocation, should - // revisit and verify we can just use one (almost certainly) - clickHandler = self._breadcrumbEventHandler('click'); - keypressHandler = self._keypressEventHandler(); - before = function(evt) { - // need to intercept every DOM event in `before` argument, in case that - // same wrapped method is re-used for different events (e.g. mousemove THEN click) - // see #724 - if (!evt) return; - - var eventType; - try { - eventType = evt.type; - } catch (e) { - // just accessing event properties can throw an exception in some rare circumstances - // see: https://github.com/getsentry/raven-js/issues/838 - return; - } - if (eventType === 'click') return clickHandler(evt); - else if (eventType === 'keypress') - return keypressHandler(evt); - }; - } - return orig.call( - this, - evtName, - self.wrap(fn, undefined, before), - capture, - secure - ); - }; - }, - wrappedBuiltIns - ); - fill( - proto, - 'removeEventListener', - function(orig) { - return function(evt, fn, capture, secure) { - try { - fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn); - } catch (e) { - // ignore, accessing __raven_wrapper__ will throw in some Selenium environments - } - return orig.call(this, evt, fn, capture, secure); - }; - }, - wrappedBuiltIns - ); + // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` + // so that we don't have more than one wrapper function + var before, clickHandler, keypressHandler; + + if ( + autoBreadcrumbs && + autoBreadcrumbs.dom && + (global === 'EventTarget' || global === 'Node') + ) { + // NOTE: generating multiple handlers per addEventListener invocation, should + // revisit and verify we can just use one (almost certainly) + clickHandler = self._breadcrumbEventHandler('click'); + keypressHandler = self._keypressEventHandler(); + before = function(evt) { + // need to intercept every DOM event in `before` argument, in case that + // same wrapped method is re-used for different events (e.g. mousemove THEN click) + // see #724 + if (!evt) return; + + var eventType; + try { + eventType = evt.type; + } catch (e) { + // just accessing event properties can throw an exception in some rare circumstances + // see: https://github.com/getsentry/raven-js/issues/838 + return; } - } + if (eventType === 'click') return clickHandler(evt); + else if (eventType === 'keypress') return keypressHandler(evt); + }; + } + return orig.call( + this, + evtName, + self.wrap(fn, undefined, before), + capture, + secure + ); + }; + }, + wrappedBuiltIns + ); + fill( + proto, + 'removeEventListener', + function(orig) { + return function(evt, fn, capture, secure) { + try { + fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn); + } catch (e) { + // ignore, accessing __raven_wrapper__ will throw in some Selenium environments + } + return orig.call(this, evt, fn, capture, secure); + }; + }, + wrappedBuiltIns + ); + } + } - fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns); - fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns); - if (_window.requestAnimationFrame) { - fill( - _window, - 'requestAnimationFrame', - function(orig) { - return function(cb) { - return orig(self.wrap(cb)); - }; - }, - wrappedBuiltIns - ); - } + fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns); + fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns); + if (_window.requestAnimationFrame) { + fill( + _window, + 'requestAnimationFrame', + function(orig) { + return function(cb) { + return orig(self.wrap(cb)); + }; + }, + wrappedBuiltIns + ); + } - // event targets borrowed from bugsnag-js: - // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 - var eventTargets = [ - 'EventTarget', - 'Window', - 'Node', - 'ApplicationCache', - 'AudioTrackList', - 'ChannelMergerNode', - 'CryptoOperation', - 'EventSource', - 'FileReader', - 'HTMLUnknownElement', - 'IDBDatabase', - 'IDBRequest', - 'IDBTransaction', - 'KeyOperation', - 'MediaController', - 'MessagePort', - 'ModalWindow', - 'Notification', - 'SVGElementInstance', - 'Screen', - 'TextTrack', - 'TextTrackCue', - 'TextTrackList', - 'WebSocket', - 'WebSocketWorker', - 'Worker', - 'XMLHttpRequest', - 'XMLHttpRequestEventTarget', - 'XMLHttpRequestUpload' - ]; - for (var i = 0; i < eventTargets.length; i++) { - wrapEventTarget(eventTargets[i]); - } - }, + // event targets borrowed from bugsnag-js: + // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 + var eventTargets = [ + 'EventTarget', + 'Window', + 'Node', + 'ApplicationCache', + 'AudioTrackList', + 'ChannelMergerNode', + 'CryptoOperation', + 'EventSource', + 'FileReader', + 'HTMLUnknownElement', + 'IDBDatabase', + 'IDBRequest', + 'IDBTransaction', + 'KeyOperation', + 'MediaController', + 'MessagePort', + 'ModalWindow', + 'Notification', + 'SVGElementInstance', + 'Screen', + 'TextTrack', + 'TextTrackCue', + 'TextTrackList', + 'WebSocket', + 'WebSocketWorker', + 'Worker', + 'XMLHttpRequest', + 'XMLHttpRequestEventTarget', + 'XMLHttpRequestUpload' + ]; + for (var i = 0; i < eventTargets.length; i++) { + wrapEventTarget(eventTargets[i]); + } + }, - /** + /** * Instrument browser built-ins w/ breadcrumb capturing * - XMLHttpRequests * - DOM interactions (click/typing) @@ -1238,546 +1202,515 @@ * * Can be disabled or individually configured via the `autoBreadcrumbs` config option */ - _instrumentBreadcrumbs: function() { - var self = this; - var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; + _instrumentBreadcrumbs: function() { + var self = this; + var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; - var wrappedBuiltIns = self._wrappedBuiltIns; + var wrappedBuiltIns = self._wrappedBuiltIns; - function wrapProp(prop, xhr) { - if (prop in xhr && isFunction(xhr[prop])) { - fill(xhr, prop, function(orig) { - return self.wrap(orig); - }); // intentionally don't track filled methods on XHR instances - } - } + function wrapProp(prop, xhr) { + if (prop in xhr && isFunction(xhr[prop])) { + fill(xhr, prop, function(orig) { + return self.wrap(orig); + }); // intentionally don't track filled methods on XHR instances + } + } - if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) { - var xhrproto = XMLHttpRequest.prototype; - fill( - xhrproto, - 'open', - function(origOpen) { - return function(method, url) { - // preserve arity - - // if Sentry key appears in URL, don't capture - if (isString(url) && url.indexOf(self._globalKey) === -1) { - this.__raven_xhr = { - method: method, - url: url, - status_code: null - }; - } - - return origOpen.apply(this, arguments); - }; - }, - wrappedBuiltIns - ); - - fill( - xhrproto, - 'send', - function(origSend) { - return function(data) { - // preserve arity - var xhr = this; - - function onreadystatechangeHandler() { - if (xhr.__raven_xhr && xhr.readyState === 4) { - try { - // touching statusCode in some platforms throws - // an exception - xhr.__raven_xhr.status_code = xhr.status; - } catch (e) { - /* do nothing */ - } - - self.captureBreadcrumb({ - type: 'http', - category: 'xhr', - data: xhr.__raven_xhr - }); - } - } - - var props = ['onload', 'onerror', 'onprogress']; - for (var j = 0; j < props.length; j++) { - wrapProp(props[j], xhr); - } - - if ( - 'onreadystatechange' in xhr && - isFunction(xhr.onreadystatechange) - ) { - fill( - xhr, - 'onreadystatechange', - function(orig) { - return self.wrap( - orig, - undefined, - onreadystatechangeHandler - ); - } /* intentionally don't track this instrumentation */ - ); - } else { - // if onreadystatechange wasn't actually set by the page on this xhr, we - // are free to set our own and capture the breadcrumb - xhr.onreadystatechange = onreadystatechangeHandler; - } - - return origSend.apply(this, arguments); - }; - }, - wrappedBuiltIns - ); - } + if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) { + var xhrproto = XMLHttpRequest.prototype; + fill( + xhrproto, + 'open', + function(origOpen) { + return function(method, url) { + // preserve arity + + // if Sentry key appears in URL, don't capture + if (isString(url) && url.indexOf(self._globalKey) === -1) { + this.__raven_xhr = { + method: method, + url: url, + status_code: null + }; + } - if (autoBreadcrumbs.xhr && 'fetch' in _window) { - fill( - _window, - 'fetch', - function(origFetch) { - return function(fn, t) { - // preserve arity - // Make a copy of the arguments to prevent deoptimization - // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments - var args = new Array(arguments.length); - for (var i = 0; i < args.length; ++i) { - args[i] = arguments[i]; - } - - var fetchInput = args[0]; - var method = 'GET'; - var url; - - if (typeof fetchInput === 'string') { - url = fetchInput; - } else { - url = fetchInput.url; - if (fetchInput.method) { - method = fetchInput.method; - } - } - - if (args[1] && args[1].method) { - method = args[1].method; - } - - var fetchData = { - method: method, - url: url, - status_code: null - }; - - self.captureBreadcrumb({ - type: 'http', - category: 'fetch', - data: fetchData - }); - - return origFetch.apply(this, args).then(function(response) { - fetchData.status_code = response.status; - - return response; - }); - }; - }, - wrappedBuiltIns - ); + return origOpen.apply(this, arguments); + }; + }, + wrappedBuiltIns + ); + + fill( + xhrproto, + 'send', + function(origSend) { + return function(data) { + // preserve arity + var xhr = this; + + function onreadystatechangeHandler() { + if (xhr.__raven_xhr && xhr.readyState === 4) { + try { + // touching statusCode in some platforms throws + // an exception + xhr.__raven_xhr.status_code = xhr.status; + } catch (e) { + /* do nothing */ } - // Capture breadcrumbs from any click that is unhandled / bubbled up all the way - // to the document. Do this before we instrument addEventListener. - if (autoBreadcrumbs.dom && this._hasDocument) { - if (_document.addEventListener) { - _document.addEventListener( - 'click', - self._breadcrumbEventHandler('click'), - false - ); - _document.addEventListener( - 'keypress', - self._keypressEventHandler(), - false - ); - } else { - // IE8 Compatibility - _document.attachEvent( - 'onclick', - self._breadcrumbEventHandler('click') - ); - _document.attachEvent('onkeypress', self._keypressEventHandler()); - } - } + self.captureBreadcrumb({ + type: 'http', + category: 'xhr', + data: xhr.__raven_xhr + }); + } + } - // record navigation (URL) changes - // NOTE: in Chrome App environment, touching history.pushState, *even inside - // a try/catch block*, will cause Chrome to output an error to console.error - // borrowed from: https://github.com/angular/angular.js/pull/13945/files - var chrome = _window.chrome; - var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; - var hasPushState = - !isChromePackagedApp && _window.history && history.pushState; - if (autoBreadcrumbs.location && hasPushState) { - // TODO: remove onpopstate handler on uninstall() - var oldOnPopState = _window.onpopstate; - _window.onpopstate = function() { - var currentHref = self._location.href; - self._captureUrlChange(self._lastHref, currentHref); - - if (oldOnPopState) { - return oldOnPopState.apply(this, arguments); - } - }; - - fill( - history, - 'pushState', - function(origPushState) { - // note history.pushState.length is 0; intentionally not declaring - // params to preserve 0 arity - return function(/* state, title, url */) { - var url = arguments.length > 2 ? arguments[2] : undefined; - - // url argument is optional - if (url) { - // coerce to string (this is what pushState does) - self._captureUrlChange(self._lastHref, url + ''); - } - - return origPushState.apply(this, arguments); - }; - }, - wrappedBuiltIns - ); - } + var props = ['onload', 'onerror', 'onprogress']; + for (var j = 0; j < props.length; j++) { + wrapProp(props[j], xhr); + } - if (autoBreadcrumbs.console && 'console' in _window && console.log) { - // console - var consoleMethodCallback = function(msg, data) { - self.captureBreadcrumb({ - message: msg, - level: data.level, - category: 'console' - }); - }; - - each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) { - wrapConsoleMethod(console, level, consoleMethodCallback); - }); - } - }, + if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) { + fill( + xhr, + 'onreadystatechange', + function(orig) { + return self.wrap(orig, undefined, onreadystatechangeHandler); + } /* intentionally don't track this instrumentation */ + ); + } else { + // if onreadystatechange wasn't actually set by the page on this xhr, we + // are free to set our own and capture the breadcrumb + xhr.onreadystatechange = onreadystatechangeHandler; + } + + return origSend.apply(this, arguments); + }; + }, + wrappedBuiltIns + ); + } - _restoreBuiltIns: function() { - // restore any wrapped builtins - var builtin; - while (this._wrappedBuiltIns.length) { - builtin = this._wrappedBuiltIns.shift(); + if (autoBreadcrumbs.xhr && 'fetch' in _window) { + fill( + _window, + 'fetch', + function(origFetch) { + return function(fn, t) { + // preserve arity + // Make a copy of the arguments to prevent deoptimization + // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } - var obj = builtin[0], - name = builtin[1], - orig = builtin[2]; + var fetchInput = args[0]; + var method = 'GET'; + var url; - obj[name] = orig; - } - }, + if (typeof fetchInput === 'string') { + url = fetchInput; + } else if ('Request' in _window && fetchInput instanceof _window.Request) { + url = fetchInput.url; + if (fetchInput.method) { + method = fetchInput.method; + } + } else { + url = '' + fetchInput; + } - _drainPlugins: function() { - var self = this; + if (args[1] && args[1].method) { + method = args[1].method; + } - // FIX ME TODO - each(this._plugins, function(_, plugin) { - var installer = plugin[0]; - var args = plugin[1]; - installer.apply(self, [self].concat(args)); - }); - }, + var fetchData = { + method: method, + url: url, + status_code: null + }; - _parseDSN: function(str) { - var m = dsnPattern.exec(str), - dsn = {}, - i = 7; + self.captureBreadcrumb({ + type: 'http', + category: 'fetch', + data: fetchData + }); - try { - while (i--) dsn[dsnKeys[i]] = m[i] || ''; - } catch (e) { - throw new RavenConfigError('Invalid DSN: ' + str); - } + return origFetch.apply(this, args).then(function(response) { + fetchData.status_code = response.status; - if (dsn.pass && !this._globalOptions.allowSecretKey) { - throw new RavenConfigError( - 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' - ); - } + return response; + }); + }; + }, + wrappedBuiltIns + ); + } - return dsn; - }, + // Capture breadcrumbs from any click that is unhandled / bubbled up all the way + // to the document. Do this before we instrument addEventListener. + if (autoBreadcrumbs.dom && this._hasDocument) { + if (_document.addEventListener) { + _document.addEventListener('click', self._breadcrumbEventHandler('click'), false); + _document.addEventListener('keypress', self._keypressEventHandler(), false); + } else { + // IE8 Compatibility + _document.attachEvent('onclick', self._breadcrumbEventHandler('click')); + _document.attachEvent('onkeypress', self._keypressEventHandler()); + } + } - _getGlobalServer: function(uri) { - // assemble the endpoint from the uri pieces - var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : ''); + // record navigation (URL) changes + // NOTE: in Chrome App environment, touching history.pushState, *even inside + // a try/catch block*, will cause Chrome to output an error to console.error + // borrowed from: https://github.com/angular/angular.js/pull/13945/files + var chrome = _window.chrome; + var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; + var hasPushAndReplaceState = + !isChromePackagedApp && + _window.history && + history.pushState && + history.replaceState; + if (autoBreadcrumbs.location && hasPushAndReplaceState) { + // TODO: remove onpopstate handler on uninstall() + var oldOnPopState = _window.onpopstate; + _window.onpopstate = function() { + var currentHref = self._location.href; + self._captureUrlChange(self._lastHref, currentHref); + + if (oldOnPopState) { + return oldOnPopState.apply(this, arguments); + } + }; + + var historyReplacementFunction = function(origHistFunction) { + // note history.pushState.length is 0; intentionally not declaring + // params to preserve 0 arity + return function(/* state, title, url */) { + var url = arguments.length > 2 ? arguments[2] : undefined; + + // url argument is optional + if (url) { + // coerce to string (this is what pushState does) + self._captureUrlChange(self._lastHref, url + ''); + } - if (uri.protocol) { - globalServer = uri.protocol + ':' + globalServer; - } - return globalServer; - }, + return origHistFunction.apply(this, arguments); + }; + }; - _handleOnErrorStackInfo: function() { - // if we are intentionally ignoring errors via onerror, bail out - if (!this._ignoreOnError) { - this._handleStackInfo.apply(this, arguments); - } - }, + fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns); + fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns); + } - _handleStackInfo: function(stackInfo, options) { - var frames = this._prepareFrames(stackInfo, options); + if (autoBreadcrumbs.console && 'console' in _window && console.log) { + // console + var consoleMethodCallback = function(msg, data) { + self.captureBreadcrumb({ + message: msg, + level: data.level, + category: 'console' + }); + }; + + each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) { + wrapConsoleMethod(console, level, consoleMethodCallback); + }); + } + }, - this._triggerEvent('handle', { - stackInfo: stackInfo, - options: options - }); + _restoreBuiltIns: function() { + // restore any wrapped builtins + var builtin; + while (this._wrappedBuiltIns.length) { + builtin = this._wrappedBuiltIns.shift(); - this._processException( - stackInfo.name, - stackInfo.message, - stackInfo.url, - stackInfo.lineno, - frames, - options - ); - }, - - _prepareFrames: function(stackInfo, options) { - var self = this; - var frames = []; - if (stackInfo.stack && stackInfo.stack.length) { - each(stackInfo.stack, function(i, stack) { - var frame = self._normalizeFrame(stack, stackInfo.url); - if (frame) { - frames.push(frame); - } - }); - - // e.g. frames captured via captureMessage throw - if (options && options.trimHeadFrames) { - for ( - var j = 0; - j < options.trimHeadFrames && j < frames.length; - j++ - ) { - frames[j].in_app = false; - } - } - } - frames = frames.slice(0, this._globalOptions.stackTraceLimit); - return frames; - }, - - _normalizeFrame: function(frame, stackInfoUrl) { - // normalize the frames data - var normalized = { - filename: frame.url, - lineno: frame.line, - colno: frame.column, - function: frame.func || '?' - }; + var obj = builtin[0], + name = builtin[1], + orig = builtin[2]; - // Case when we don't have any information about the error - // E.g. throwing a string or raw object, instead of an `Error` in Firefox - // Generating synthetic error doesn't add any value here - // - // We should probably somehow let a user know that they should fix their code - if (!frame.url) { - normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler - } + obj[name] = orig; + } + }, + + _drainPlugins: function() { + var self = this; + + // FIX ME TODO + each(this._plugins, function(_, plugin) { + var installer = plugin[0]; + var args = plugin[1]; + installer.apply(self, [self].concat(args)); + }); + }, + + _parseDSN: function(str) { + var m = dsnPattern.exec(str), + dsn = {}, + i = 7; + + try { + while (i--) dsn[dsnKeys[i]] = m[i] || ''; + } catch (e) { + throw new RavenConfigError('Invalid DSN: ' + str); + } - normalized.in_app = !// determine if an exception came from outside of our app - // first we check the global includePaths list. - ( - (!!this._globalOptions.includePaths.test && - !this._globalOptions.includePaths.test(normalized.filename)) || - // Now we check for fun, if the function name is Raven or TraceKit - /(Raven|TraceKit)\./.test(normalized['function']) || - // finally, we do a last ditch effort and check for raven.min.js - /raven\.(min\.)?js$/.test(normalized.filename) - ); - - return normalized; - }, - - _processException: function( - type, - message, - fileurl, - lineno, - frames, - options - ) { - var testString = (type || '') + ': ' + (message || ''); - - if ( - !!this._globalOptions.ignoreErrors.test && - this._globalOptions.ignoreErrors.test(testString) - ) - return; - - var stacktrace; - - if (frames && frames.length) { - fileurl = frames[0].filename || fileurl; - // Sentry expects frames oldest to newest - // and JS sends them as newest to oldest - frames.reverse(); - stacktrace = {frames: frames}; - } else if (fileurl) { - stacktrace = { - frames: [ - { - filename: fileurl, - lineno: lineno, - in_app: true - } - ] - }; - } + if (dsn.pass && !this._globalOptions.allowSecretKey) { + throw new RavenConfigError( + 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' + ); + } - if ( - !!this._globalOptions.ignoreUrls.test && - this._globalOptions.ignoreUrls.test(fileurl) - ) - return; - if ( - !!this._globalOptions.whitelistUrls.test && - !this._globalOptions.whitelistUrls.test(fileurl) - ) - return; - - var data = objectMerge( - { - // sentry.interfaces.Exception - exception: { - values: [ - { - type: type, - value: message, - stacktrace: stacktrace - } - ] - }, - culprit: fileurl - }, - options - ); - - // Fire away! - this._send(data); - }, - - _trimPacket: function(data) { - // For now, we only want to truncate the two different messages - // but this could/should be expanded to just trim everything - var max = this._globalOptions.maxMessageLength; - if (data.message) { - data.message = truncate(data.message, max); - } - if (data.exception) { - var exception = data.exception.values[0]; - exception.value = truncate(exception.value, max); - } + return dsn; + }, - var request = data.request; - if (request) { - if (request.url) { - request.url = truncate(request.url, this._globalOptions.maxUrlLength); - } - if (request.Referer) { - request.Referer = truncate( - request.Referer, - this._globalOptions.maxUrlLength - ); - } - } + _getGlobalServer: function(uri) { + // assemble the endpoint from the uri pieces + var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : ''); + + if (uri.protocol) { + globalServer = uri.protocol + ':' + globalServer; + } + return globalServer; + }, + + _handleOnErrorStackInfo: function() { + // if we are intentionally ignoring errors via onerror, bail out + if (!this._ignoreOnError) { + this._handleStackInfo.apply(this, arguments); + } + }, + + _handleStackInfo: function(stackInfo, options) { + var frames = this._prepareFrames(stackInfo, options); + + this._triggerEvent('handle', { + stackInfo: stackInfo, + options: options + }); + + this._processException( + stackInfo.name, + stackInfo.message, + stackInfo.url, + stackInfo.lineno, + frames, + options + ); + }, + + _prepareFrames: function(stackInfo, options) { + var self = this; + var frames = []; + if (stackInfo.stack && stackInfo.stack.length) { + each(stackInfo.stack, function(i, stack) { + var frame = self._normalizeFrame(stack, stackInfo.url); + if (frame) { + frames.push(frame); + } + }); + + // e.g. frames captured via captureMessage throw + if (options && options.trimHeadFrames) { + for (var j = 0; j < options.trimHeadFrames && j < frames.length; j++) { + frames[j].in_app = false; + } + } + } + frames = frames.slice(0, this._globalOptions.stackTraceLimit); + return frames; + }, + + _normalizeFrame: function(frame, stackInfoUrl) { + // normalize the frames data + var normalized = { + filename: frame.url, + lineno: frame.line, + colno: frame.column, + function: frame.func || '?' + }; + + // Case when we don't have any information about the error + // E.g. throwing a string or raw object, instead of an `Error` in Firefox + // Generating synthetic error doesn't add any value here + // + // We should probably somehow let a user know that they should fix their code + if (!frame.url) { + normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler + } + + normalized.in_app = !// determine if an exception came from outside of our app + // first we check the global includePaths list. + ( + (!!this._globalOptions.includePaths.test && + !this._globalOptions.includePaths.test(normalized.filename)) || + // Now we check for fun, if the function name is Raven or TraceKit + /(Raven|TraceKit)\./.test(normalized['function']) || + // finally, we do a last ditch effort and check for raven.min.js + /raven\.(min\.)?js$/.test(normalized.filename) + ); + + return normalized; + }, + + _processException: function(type, message, fileurl, lineno, frames, options) { + var prefixedMessage = (type ? type + ': ' : '') + (message || ''); + if ( + !!this._globalOptions.ignoreErrors.test && + (this._globalOptions.ignoreErrors.test(message) || + this._globalOptions.ignoreErrors.test(prefixedMessage)) + ) { + return; + } + + var stacktrace; + + if (frames && frames.length) { + fileurl = frames[0].filename || fileurl; + // Sentry expects frames oldest to newest + // and JS sends them as newest to oldest + frames.reverse(); + stacktrace = {frames: frames}; + } else if (fileurl) { + stacktrace = { + frames: [ + { + filename: fileurl, + lineno: lineno, + in_app: true + } + ] + }; + } + + if ( + !!this._globalOptions.ignoreUrls.test && + this._globalOptions.ignoreUrls.test(fileurl) + ) { + return; + } - if (data.breadcrumbs && data.breadcrumbs.values) - this._trimBreadcrumbs(data.breadcrumbs); + if ( + !!this._globalOptions.whitelistUrls.test && + !this._globalOptions.whitelistUrls.test(fileurl) + ) { + return; + } + + var data = objectMerge( + { + // sentry.interfaces.Exception + exception: { + values: [ + { + type: type, + value: message, + stacktrace: stacktrace + } + ] + }, + culprit: fileurl + }, + options + ); + + // Fire away! + this._send(data); + }, + + _trimPacket: function(data) { + // For now, we only want to truncate the two different messages + // but this could/should be expanded to just trim everything + var max = this._globalOptions.maxMessageLength; + if (data.message) { + data.message = truncate(data.message, max); + } + if (data.exception) { + var exception = data.exception.values[0]; + exception.value = truncate(exception.value, max); + } - return data; - }, + var request = data.request; + if (request) { + if (request.url) { + request.url = truncate(request.url, this._globalOptions.maxUrlLength); + } + if (request.Referer) { + request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength); + } + } - /** + if (data.breadcrumbs && data.breadcrumbs.values) + this._trimBreadcrumbs(data.breadcrumbs); + + return data; + }, + + /** * Truncate breadcrumb values (right now just URLs) */ - _trimBreadcrumbs: function(breadcrumbs) { - // known breadcrumb properties with urls - // TODO: also consider arbitrary prop values that start with (https?)?:// - var urlProps = ['to', 'from', 'url'], - urlProp, - crumb, - data; - - for (var i = 0; i < breadcrumbs.values.length; ++i) { - crumb = breadcrumbs.values[i]; - if ( - !crumb.hasOwnProperty('data') || - !isObject(crumb.data) || - objectFrozen(crumb.data) - ) - continue; - - data = objectMerge({}, crumb.data); - for (var j = 0; j < urlProps.length; ++j) { - urlProp = urlProps[j]; - if (data.hasOwnProperty(urlProp) && data[urlProp]) { - data[urlProp] = truncate( - data[urlProp], - this._globalOptions.maxUrlLength - ); - } - } - breadcrumbs.values[i].data = data; - } - }, + _trimBreadcrumbs: function(breadcrumbs) { + // known breadcrumb properties with urls + // TODO: also consider arbitrary prop values that start with (https?)?:// + var urlProps = ['to', 'from', 'url'], + urlProp, + crumb, + data; + + for (var i = 0; i < breadcrumbs.values.length; ++i) { + crumb = breadcrumbs.values[i]; + if ( + !crumb.hasOwnProperty('data') || + !isObject(crumb.data) || + objectFrozen(crumb.data) + ) + continue; + + data = objectMerge({}, crumb.data); + for (var j = 0; j < urlProps.length; ++j) { + urlProp = urlProps[j]; + if (data.hasOwnProperty(urlProp) && data[urlProp]) { + data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength); + } + } + breadcrumbs.values[i].data = data; + } + }, - _getHttpData: function() { - if (!this._hasNavigator && !this._hasDocument) return; - var httpData = {}; + _getHttpData: function() { + if (!this._hasNavigator && !this._hasDocument) return; + var httpData = {}; - if (this._hasNavigator && _navigator.userAgent) { - httpData.headers = { - 'User-Agent': navigator.userAgent - }; - } + if (this._hasNavigator && _navigator.userAgent) { + httpData.headers = { + 'User-Agent': navigator.userAgent + }; + } - if (this._hasDocument) { - if (_document.location && _document.location.href) { - httpData.url = _document.location.href; - } - if (_document.referrer) { - if (!httpData.headers) httpData.headers = {}; - httpData.headers.Referer = _document.referrer; - } - } + if (this._hasDocument) { + if (_document.location && _document.location.href) { + httpData.url = _document.location.href; + } + if (_document.referrer) { + if (!httpData.headers) httpData.headers = {}; + httpData.headers.Referer = _document.referrer; + } + } - return httpData; - }, + return httpData; + }, - _resetBackoff: function() { - this._backoffDuration = 0; - this._backoffStart = null; - }, + _resetBackoff: function() { + this._backoffDuration = 0; + this._backoffStart = null; + }, - _shouldBackoff: function() { - return ( - this._backoffDuration && - now() - this._backoffStart < this._backoffDuration - ); - }, + _shouldBackoff: function() { + return this._backoffDuration && now() - this._backoffStart < this._backoffDuration; + }, - /** + /** * Returns true if the in-process data payload matches the signature * of the previously-sent data * @@ -1786,362 +1719,449 @@ * other old browsers). This can take the form of an "exception" * data object with a single frame (derived from the onerror args). */ - _isRepeatData: function(current) { - var last = this._lastData; - - if ( - !last || - current.message !== last.message || // defined for captureMessage - current.culprit !== last.culprit // defined for captureException/onerror - ) - return false; - - // Stacktrace interface (i.e. from captureMessage) - if (current.stacktrace || last.stacktrace) { - return isSameStacktrace(current.stacktrace, last.stacktrace); - } else if (current.exception || last.exception) { - // Exception interface (i.e. from captureException/onerror) - return isSameException(current.exception, last.exception); - } + _isRepeatData: function(current) { + var last = this._lastData; + + if ( + !last || + current.message !== last.message || // defined for captureMessage + current.culprit !== last.culprit // defined for captureException/onerror + ) + return false; + + // Stacktrace interface (i.e. from captureMessage) + if (current.stacktrace || last.stacktrace) { + return isSameStacktrace(current.stacktrace, last.stacktrace); + } else if (current.exception || last.exception) { + // Exception interface (i.e. from captureException/onerror) + return isSameException(current.exception, last.exception); + } - return true; - }, + return true; + }, - _setBackoffState: function(request) { - // If we are already in a backoff state, don't change anything - if (this._shouldBackoff()) { - return; - } + _setBackoffState: function(request) { + // If we are already in a backoff state, don't change anything + if (this._shouldBackoff()) { + return; + } - var status = request.status; + var status = request.status; + + // 400 - project_id doesn't exist or some other fatal + // 401 - invalid/revoked dsn + // 429 - too many requests + if (!(status === 400 || status === 401 || status === 429)) return; + + var retry; + try { + // If Retry-After is not in Access-Control-Expose-Headers, most + // browsers will throw an exception trying to access it + retry = request.getResponseHeader('Retry-After'); + retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds + } catch (e) { + /* eslint no-empty:0 */ + } - // 400 - project_id doesn't exist or some other fatal - // 401 - invalid/revoked dsn - // 429 - too many requests - if (!(status === 400 || status === 401 || status === 429)) return; + this._backoffDuration = retry + ? // If Sentry server returned a Retry-After value, use it + retry + : // Otherwise, double the last backoff duration (starts at 1 sec) + this._backoffDuration * 2 || 1000; - var retry; - try { - // If Retry-After is not in Access-Control-Expose-Headers, most - // browsers will throw an exception trying to access it - retry = request.getResponseHeader('Retry-After'); - retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds - } catch (e) { - /* eslint no-empty:0 */ - } + this._backoffStart = now(); + }, - this._backoffDuration = retry - ? // If Sentry server returned a Retry-After value, use it - retry - : // Otherwise, double the last backoff duration (starts at 1 sec) - this._backoffDuration * 2 || 1000; + _send: function(data) { + var globalOptions = this._globalOptions; - this._backoffStart = now(); - }, + var baseData = { + project: this._globalProject, + logger: globalOptions.logger, + platform: 'javascript' + }, + httpData = this._getHttpData(); - _send: function(data) { - var globalOptions = this._globalOptions; + if (httpData) { + baseData.request = httpData; + } - var baseData = { - project: this._globalProject, - logger: globalOptions.logger, - platform: 'javascript' - }, - httpData = this._getHttpData(); + // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload + if (data.trimHeadFrames) delete data.trimHeadFrames; - if (httpData) { - baseData.request = httpData; - } + data = objectMerge(baseData, data); - // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload - if (data.trimHeadFrames) delete data.trimHeadFrames; - - data = objectMerge(baseData, data); - - // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge - data.tags = objectMerge( - objectMerge({}, this._globalContext.tags), - data.tags - ); - data.extra = objectMerge( - objectMerge({}, this._globalContext.extra), - data.extra - ); - - // Send along our own collected metadata with extra - data.extra['session:duration'] = now() - this._startTime; - - if (this._breadcrumbs && this._breadcrumbs.length > 0) { - // intentionally make shallow copy so that additions - // to breadcrumbs aren't accidentally sent in this request - data.breadcrumbs = { - values: [].slice.call(this._breadcrumbs, 0) - }; - } + // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge + data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags); + data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra); - // If there are no tags/extra, strip the key from the payload alltogther. - if (isEmptyObject(data.tags)) delete data.tags; + // Send along our own collected metadata with extra + data.extra['session:duration'] = now() - this._startTime; - if (this._globalContext.user) { - // sentry.interfaces.User - data.user = this._globalContext.user; - } + if (this._breadcrumbs && this._breadcrumbs.length > 0) { + // intentionally make shallow copy so that additions + // to breadcrumbs aren't accidentally sent in this request + data.breadcrumbs = { + values: [].slice.call(this._breadcrumbs, 0) + }; + } - // Include the environment if it's defined in globalOptions - if (globalOptions.environment) - data.environment = globalOptions.environment; + // If there are no tags/extra, strip the key from the payload alltogther. + if (isEmptyObject(data.tags)) delete data.tags; - // Include the release if it's defined in globalOptions - if (globalOptions.release) data.release = globalOptions.release; + if (this._globalContext.user) { + // sentry.interfaces.User + data.user = this._globalContext.user; + } - // Include server_name if it's defined in globalOptions - if (globalOptions.serverName) data.server_name = globalOptions.serverName; + // Include the environment if it's defined in globalOptions + if (globalOptions.environment) data.environment = globalOptions.environment; - if (isFunction(globalOptions.dataCallback)) { - data = globalOptions.dataCallback(data) || data; - } + // Include the release if it's defined in globalOptions + if (globalOptions.release) data.release = globalOptions.release; - // Why?????????? - if (!data || isEmptyObject(data)) { - return; - } + // Include server_name if it's defined in globalOptions + if (globalOptions.serverName) data.server_name = globalOptions.serverName; - // Check if the request should be filtered or not - if ( - isFunction(globalOptions.shouldSendCallback) && - !globalOptions.shouldSendCallback(data) - ) { - return; - } + if (isFunction(globalOptions.dataCallback)) { + data = globalOptions.dataCallback(data) || data; + } - // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), - // so drop requests until "cool-off" period has elapsed. - if (this._shouldBackoff()) { - this._logDebug('warn', 'Raven dropped error due to backoff: ', data); - return; - } + // Why?????????? + if (!data || isEmptyObject(data)) { + return; + } - if (typeof globalOptions.sampleRate === 'number') { - if (Math.random() < globalOptions.sampleRate) { - this._sendProcessedPayload(data); - } - } else { - this._sendProcessedPayload(data); - } - }, + // Check if the request should be filtered or not + if ( + isFunction(globalOptions.shouldSendCallback) && + !globalOptions.shouldSendCallback(data) + ) { + return; + } + + // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), + // so drop requests until "cool-off" period has elapsed. + if (this._shouldBackoff()) { + this._logDebug('warn', 'Raven dropped error due to backoff: ', data); + return; + } - _getUuid: function() { - return uuid4(); - }, + if (typeof globalOptions.sampleRate === 'number') { + if (Math.random() < globalOptions.sampleRate) { + this._sendProcessedPayload(data); + } + } else { + this._sendProcessedPayload(data); + } + }, - _sendProcessedPayload: function(data, callback) { - var self = this; - var globalOptions = this._globalOptions; + _getUuid: function() { + return uuid4(); + }, - if (!this.isSetup()) return; + _sendProcessedPayload: function(data, callback) { + var self = this; + var globalOptions = this._globalOptions; - // Try and clean up the packet before sending by truncating long values - data = this._trimPacket(data); + if (!this.isSetup()) return; - // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, - // but this would require copying an un-truncated copy of the data packet, which can be - // arbitrarily deep (extra_data) -- could be worthwhile? will revisit - if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { - this._logDebug('warn', 'Raven dropped repeat event: ', data); - return; - } + // Try and clean up the packet before sending by truncating long values + data = this._trimPacket(data); - // Send along an event_id if not explicitly passed. - // This event_id can be used to reference the error within Sentry itself. - // Set lastEventId after we know the error should actually be sent - this._lastEventId = data.event_id || (data.event_id = this._getUuid()); + // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, + // but this would require copying an un-truncated copy of the data packet, which can be + // arbitrarily deep (extra_data) -- could be worthwhile? will revisit + if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { + this._logDebug('warn', 'Raven dropped repeat event: ', data); + return; + } - // Store outbound payload after trim - this._lastData = data; + // Send along an event_id if not explicitly passed. + // This event_id can be used to reference the error within Sentry itself. + // Set lastEventId after we know the error should actually be sent + this._lastEventId = data.event_id || (data.event_id = this._getUuid()); - this._logDebug('debug', 'Raven about to send:', data); + // Store outbound payload after trim + this._lastData = data; - var auth = { - sentry_version: '7', - sentry_client: 'raven-js/' + this.VERSION, - sentry_key: this._globalKey - }; + this._logDebug('debug', 'Raven about to send:', data); - if (this._globalSecret) { - auth.sentry_secret = this._globalSecret; - } + var auth = { + sentry_version: '7', + sentry_client: 'raven-js/' + this.VERSION, + sentry_key: this._globalKey + }; - var exception = data.exception && data.exception.values[0]; - this.captureBreadcrumb({ - category: 'sentry', - message: exception - ? (exception.type ? exception.type + ': ' : '') + exception.value - : data.message, - event_id: data.event_id, - level: data.level || 'error' // presume error unless specified - }); + if (this._globalSecret) { + auth.sentry_secret = this._globalSecret; + } - var url = this._globalEndpoint; - (globalOptions.transport || this._makeRequest).call(this, { - url: url, - auth: auth, - data: data, - options: globalOptions, - onSuccess: function success() { - self._resetBackoff(); - - self._triggerEvent('success', { - data: data, - src: url - }); - callback && callback(); - }, - onError: function failure(error) { - self._logDebug('error', 'Raven transport failed to send: ', error); - - if (error.request) { - self._setBackoffState(error.request); - } - - self._triggerEvent('failure', { - data: data, - src: url - }); - error = - error || - new Error('Raven send failed (no additional details provided)'); - callback && callback(error); - } - }); - }, - - _makeRequest: function(opts) { - var request = _window.XMLHttpRequest && new _window.XMLHttpRequest(); - if (!request) return; - - // if browser doesn't support CORS (e.g. IE7), we are out of luck - var hasCORS = - 'withCredentials' in request || typeof XDomainRequest !== 'undefined'; - - if (!hasCORS) return; - - var url = opts.url; - - if ('withCredentials' in request) { - request.onreadystatechange = function() { - if (request.readyState !== 4) { - return; - } else if (request.status === 200) { - opts.onSuccess && opts.onSuccess(); - } else if (opts.onError) { - var err = new Error('Sentry error code: ' + request.status); - err.request = request; - opts.onError(err); - } - }; - } else { - request = new XDomainRequest(); - // xdomainrequest cannot go http -> https (or vice versa), - // so always use protocol relative - url = url.replace(/^https?:/, ''); - - // onreadystatechange not supported by XDomainRequest - if (opts.onSuccess) { - request.onload = opts.onSuccess; - } - if (opts.onError) { - request.onerror = function() { - var err = new Error('Sentry error code: XDomainRequest'); - err.request = request; - opts.onError(err); - }; - } - } + var exception = data.exception && data.exception.values[0]; + + // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy + if ( + this._globalOptions.autoBreadcrumbs && + this._globalOptions.autoBreadcrumbs.sentry + ) { + this.captureBreadcrumb({ + category: 'sentry', + message: exception + ? (exception.type ? exception.type + ': ' : '') + exception.value + : data.message, + event_id: data.event_id, + level: data.level || 'error' // presume error unless specified + }); + } - // NOTE: auth is intentionally sent as part of query string (NOT as custom - // HTTP header) so as to avoid preflight CORS requests - request.open('POST', url + '?' + urlencode(opts.auth)); - request.send(stringify(opts.data)); - }, - - _logDebug: function(level) { - if (this._originalConsoleMethods[level] && this.debug) { - // In IE<10 console methods do not have their own 'apply' method - Function.prototype.apply.call( - this._originalConsoleMethods[level], - this._originalConsole, - [].slice.call(arguments, 1) - ); - } - }, - - _mergeContext: function(key, context) { - if (isUndefined(context)) { - delete this._globalContext[key]; - } else { - this._globalContext[key] = objectMerge( - this._globalContext[key] || {}, - context - ); - } - } - }; + var url = this._globalEndpoint; + (globalOptions.transport || this._makeRequest).call(this, { + url: url, + auth: auth, + data: data, + options: globalOptions, + onSuccess: function success() { + self._resetBackoff(); + + self._triggerEvent('success', { + data: data, + src: url + }); + callback && callback(); + }, + onError: function failure(error) { + self._logDebug('error', 'Raven transport failed to send: ', error); + + if (error.request) { + self._setBackoffState(error.request); + } + + self._triggerEvent('failure', { + data: data, + src: url + }); + error = error || new Error('Raven send failed (no additional details provided)'); + callback && callback(error); + } + }); + }, + + _makeRequest: function(opts) { + var request = _window.XMLHttpRequest && new _window.XMLHttpRequest(); + if (!request) return; + + // if browser doesn't support CORS (e.g. IE7), we are out of luck + var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined'; + + if (!hasCORS) return; + + var url = opts.url; + + if ('withCredentials' in request) { + request.onreadystatechange = function() { + if (request.readyState !== 4) { + return; + } else if (request.status === 200) { + opts.onSuccess && opts.onSuccess(); + } else if (opts.onError) { + var err = new Error('Sentry error code: ' + request.status); + err.request = request; + opts.onError(err); + } + }; + } else { + request = new XDomainRequest(); + // xdomainrequest cannot go http -> https (or vice versa), + // so always use protocol relative + url = url.replace(/^https?:/, ''); + + // onreadystatechange not supported by XDomainRequest + if (opts.onSuccess) { + request.onload = opts.onSuccess; + } + if (opts.onError) { + request.onerror = function() { + var err = new Error('Sentry error code: XDomainRequest'); + err.request = request; + opts.onError(err); + }; + } + } + + // NOTE: auth is intentionally sent as part of query string (NOT as custom + // HTTP header) so as to avoid preflight CORS requests + request.open('POST', url + '?' + urlencode(opts.auth)); + request.send(stringify(opts.data)); + }, + + _logDebug: function(level) { + if (this._originalConsoleMethods[level] && this.debug) { + // In IE<10 console methods do not have their own 'apply' method + Function.prototype.apply.call( + this._originalConsoleMethods[level], + this._originalConsole, + [].slice.call(arguments, 1) + ); + } + }, + + _mergeContext: function(key, context) { + if (isUndefined(context)) { + delete this._globalContext[key]; + } else { + this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context); + } + } +}; + +// Deprecations +Raven.prototype.setUser = Raven.prototype.setUserContext; +Raven.prototype.setReleaseContext = Raven.prototype.setRelease; + +module.exports = Raven; - /*------------------------------------------------ - * utils +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"1":1,"2":2,"5":5,"6":6,"7":7}],4:[function(_dereq_,module,exports){ +(function (global){ +/** + * Enforces a single instance of the Raven client, and the + * main entry point for Raven. If you are a consumer of the + * Raven library, you SHOULD load this file (vs raven.js). + **/ + +var RavenConstructor = _dereq_(3); + +// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) +var _window = + typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; +var _Raven = _window.Raven; + +var Raven = new RavenConstructor(); + +/* + * Allow multiple versions of Raven to be installed. + * Strip Raven from the global context and returns the instance. * - * conditionally exported for test via Raven.utils - ================================================= + * @return {Raven} */ - var objectPrototype = Object.prototype; +Raven.noConflict = function() { + _window.Raven = _Raven; + return Raven; +}; + +Raven.afterLoad(); + +module.exports = Raven; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"3":3}],5:[function(_dereq_,module,exports){ +(function (global){ +var _window = + typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function isObject(what) { + return typeof what === 'object' && what !== null; +} + +// Yanked from https://git.io/vS8DV re-used under CC0 +// with some tiny modifications +function isError(value) { + switch ({}.toString.call(value)) { + case '[object Error]': + return true; + case '[object Exception]': + return true; + case '[object DOMException]': + return true; + default: + return value instanceof Error; + } +} - function isUndefined(what) { - return what === void 0; - } +function isErrorEvent(value) { + return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]'; +} - function isFunction(what) { - return typeof what === 'function'; - } +function isUndefined(what) { + return what === void 0; +} - function isString(what) { - return objectPrototype.toString.call(what) === '[object String]'; - } +function isFunction(what) { + return typeof what === 'function'; +} - function isEmptyObject(what) { - for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars - return true; - } +function isString(what) { + return Object.prototype.toString.call(what) === '[object String]'; +} - function each(obj, callback) { - var i, j; +function isArray(what) { + return Object.prototype.toString.call(what) === '[object Array]'; +} - if (isUndefined(obj.length)) { - for (i in obj) { - if (hasKey(obj, i)) { - callback.call(null, i, obj[i]); - } - } - } else { - j = obj.length; - if (j) { - for (i = 0; i < j; i++) { - callback.call(null, i, obj[i]); - } - } - } - } +function isEmptyObject(what) { + for (var _ in what) { + if (what.hasOwnProperty(_)) { + return false; + } + } + return true; +} + +function supportsErrorEvent() { + try { + new ErrorEvent(''); // eslint-disable-line no-new + return true; + } catch (e) { + return false; + } +} - function objectMerge(obj1, obj2) { - if (!obj2) { - return obj1; - } - each(obj2, function(key, value) { - obj1[key] = value; - }); - return obj1; - } +function wrappedCallback(callback) { + function dataCallback(data, original) { + var normalizedData = callback(data) || data; + if (original) { + return original(normalizedData) || normalizedData; + } + return normalizedData; + } + + return dataCallback; +} + +function each(obj, callback) { + var i, j; + + if (isUndefined(obj.length)) { + for (i in obj) { + if (hasKey(obj, i)) { + callback.call(null, i, obj[i]); + } + } + } else { + j = obj.length; + if (j) { + for (i = 0; i < j; i++) { + callback.call(null, i, obj[i]); + } + } + } +} + +function objectMerge(obj1, obj2) { + if (!obj2) { + return obj1; + } + each(obj2, function(key, value) { + obj1[key] = value; + }); + return obj1; +} - /** +/** * This function is only used for react-native. * react-native freezes object that have already been sent over the * js bridge. We need this function in order to check if the object is frozen. @@ -2149,398 +2169,298 @@ * supported because it's not relevant for other "platforms". See related issue: * https://github.com/getsentry/react-native-sentry/issues/57 */ - function objectFrozen(obj) { - if (!Object.isFrozen) { - return false; - } - return Object.isFrozen(obj); - } +function objectFrozen(obj) { + if (!Object.isFrozen) { + return false; + } + return Object.isFrozen(obj); +} - function truncate(str, max) { - return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; - } +function truncate(str, max) { + return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; +} - /** +/** * hasKey, a better form of hasOwnProperty * Example: hasKey(MainHostObject, property) === true/false * * @param {Object} host object to check property * @param {string} key to check */ - function hasKey(object, key) { - return objectPrototype.hasOwnProperty.call(object, key); - } - - function joinRegExp(patterns) { - // Combine an array of regular expressions and strings into one large regexp - // Be mad. - var sources = [], - i = 0, - len = patterns.length, - pattern; - - for (; i < len; i++) { - pattern = patterns[i]; - if (isString(pattern)) { - // If it's a string, we need to escape it - // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions - sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')); - } else if (pattern && pattern.source) { - // If it's a regexp already, we want to extract the source - sources.push(pattern.source); - } - // Intentionally skip other cases - } - return new RegExp(sources.join('|'), 'i'); - } - - function urlencode(o) { - var pairs = []; - each(o, function(key, value) { - pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); - }); - return pairs.join('&'); - } - - // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B - // intentionally using regex and not href parsing trick because React Native and other - // environments where DOM might not be available - function parseUrl(url) { - var match = url.match( - /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/ - ); - if (!match) return {}; - - // coerce to undefined values to empty string so we don't get 'undefined' - var query = match[6] || ''; - var fragment = match[8] || ''; - return { - protocol: match[2], - host: match[4], - path: match[5], - relative: match[5] + query + fragment // everything minus origin - }; - } - function uuid4() { - var crypto = _window.crypto || _window.msCrypto; - - if (!isUndefined(crypto) && crypto.getRandomValues) { - // Use window.crypto API if available - // eslint-disable-next-line no-undef - var arr = new Uint16Array(8); - crypto.getRandomValues(arr); - - // set 4 in byte 7 - arr[3] = (arr[3] & 0xfff) | 0x4000; - // set 2 most significant bits of byte 9 to '10' - arr[4] = (arr[4] & 0x3fff) | 0x8000; - - var pad = function(num) { - var v = num.toString(16); - while (v.length < 4) { - v = '0' + v; - } - return v; - }; - - return ( - pad(arr[0]) + - pad(arr[1]) + - pad(arr[2]) + - pad(arr[3]) + - pad(arr[4]) + - pad(arr[5]) + - pad(arr[6]) + - pad(arr[7]) - ); - } else { - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 - return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = (Math.random() * 16) | 0, - v = c === 'x' ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); - } - } +function hasKey(object, key) { + return Object.prototype.hasOwnProperty.call(object, key); +} + +function joinRegExp(patterns) { + // Combine an array of regular expressions and strings into one large regexp + // Be mad. + var sources = [], + i = 0, + len = patterns.length, + pattern; + + for (; i < len; i++) { + pattern = patterns[i]; + if (isString(pattern)) { + // If it's a string, we need to escape it + // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions + sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')); + } else if (pattern && pattern.source) { + // If it's a regexp already, we want to extract the source + sources.push(pattern.source); + } + // Intentionally skip other cases + } + return new RegExp(sources.join('|'), 'i'); +} + +function urlencode(o) { + var pairs = []; + each(o, function(key, value) { + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + return pairs.join('&'); +} + +// borrowed from https://tools.ietf.org/html/rfc3986#appendix-B +// intentionally using regex and not href parsing trick because React Native and other +// environments where DOM might not be available +function parseUrl(url) { + var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/); + if (!match) return {}; + + // coerce to undefined values to empty string so we don't get 'undefined' + var query = match[6] || ''; + var fragment = match[8] || ''; + return { + protocol: match[2], + host: match[4], + path: match[5], + relative: match[5] + query + fragment // everything minus origin + }; +} +function uuid4() { + var crypto = _window.crypto || _window.msCrypto; + + if (!isUndefined(crypto) && crypto.getRandomValues) { + // Use window.crypto API if available + // eslint-disable-next-line no-undef + var arr = new Uint16Array(8); + crypto.getRandomValues(arr); + + // set 4 in byte 7 + arr[3] = (arr[3] & 0xfff) | 0x4000; + // set 2 most significant bits of byte 9 to '10' + arr[4] = (arr[4] & 0x3fff) | 0x8000; + + var pad = function(num) { + var v = num.toString(16); + while (v.length < 4) { + v = '0' + v; + } + return v; + }; + + return ( + pad(arr[0]) + + pad(arr[1]) + + pad(arr[2]) + + pad(arr[3]) + + pad(arr[4]) + + pad(arr[5]) + + pad(arr[6]) + + pad(arr[7]) + ); + } else { + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 + return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (Math.random() * 16) | 0, + v = c === 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } +} - /** +/** * Given a child DOM element, returns a query-selector statement describing that * and its ancestors * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz] * @param elem * @returns {string} */ - function htmlTreeAsString(elem) { - /* eslint no-extra-parens:0*/ - var MAX_TRAVERSE_HEIGHT = 5, - MAX_OUTPUT_LEN = 80, - out = [], - height = 0, - len = 0, - separator = ' > ', - sepLength = separator.length, - nextStr; - - while (elem && height++ < MAX_TRAVERSE_HEIGHT) { - nextStr = htmlElementAsString(elem); - // bail out if - // - nextStr is the 'html' element - // - the length of the string that would be created exceeds MAX_OUTPUT_LEN - // (ignore this limit if we are on the first iteration) - if ( - nextStr === 'html' || - (height > 1 && - len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN) - ) { - break; - } +function htmlTreeAsString(elem) { + /* eslint no-extra-parens:0*/ + var MAX_TRAVERSE_HEIGHT = 5, + MAX_OUTPUT_LEN = 80, + out = [], + height = 0, + len = 0, + separator = ' > ', + sepLength = separator.length, + nextStr; + + while (elem && height++ < MAX_TRAVERSE_HEIGHT) { + nextStr = htmlElementAsString(elem); + // bail out if + // - nextStr is the 'html' element + // - the length of the string that would be created exceeds MAX_OUTPUT_LEN + // (ignore this limit if we are on the first iteration) + if ( + nextStr === 'html' || + (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN) + ) { + break; + } - out.push(nextStr); + out.push(nextStr); - len += nextStr.length; - elem = elem.parentNode; - } + len += nextStr.length; + elem = elem.parentNode; + } - return out.reverse().join(separator); - } + return out.reverse().join(separator); +} - /** +/** * Returns a simple, query-selector representation of a DOM element * e.g. [HTMLElement] => input#foo.btn[name=baz] * @param HTMLElement * @returns {string} */ - function htmlElementAsString(elem) { - var out = [], - className, - classes, - key, - attr, - i; - - if (!elem || !elem.tagName) { - return ''; - } +function htmlElementAsString(elem) { + var out = [], + className, + classes, + key, + attr, + i; + + if (!elem || !elem.tagName) { + return ''; + } - out.push(elem.tagName.toLowerCase()); - if (elem.id) { - out.push('#' + elem.id); - } + out.push(elem.tagName.toLowerCase()); + if (elem.id) { + out.push('#' + elem.id); + } - className = elem.className; - if (className && isString(className)) { - classes = className.split(/\s+/); - for (i = 0; i < classes.length; i++) { - out.push('.' + classes[i]); - } - } - var attrWhitelist = ['type', 'name', 'title', 'alt']; - for (i = 0; i < attrWhitelist.length; i++) { - key = attrWhitelist[i]; - attr = elem.getAttribute(key); - if (attr) { - out.push('[' + key + '="' + attr + '"]'); - } - } - return out.join(''); - } + className = elem.className; + if (className && isString(className)) { + classes = className.split(/\s+/); + for (i = 0; i < classes.length; i++) { + out.push('.' + classes[i]); + } + } + var attrWhitelist = ['type', 'name', 'title', 'alt']; + for (i = 0; i < attrWhitelist.length; i++) { + key = attrWhitelist[i]; + attr = elem.getAttribute(key); + if (attr) { + out.push('[' + key + '="' + attr + '"]'); + } + } + return out.join(''); +} - /** +/** * Returns true if either a OR b is truthy, but not both */ - function isOnlyOneTruthy(a, b) { - return !!(!!a ^ !!b); - } +function isOnlyOneTruthy(a, b) { + return !!(!!a ^ !!b); +} - /** +/** * Returns true if the two input exception interfaces have the same content */ - function isSameException(ex1, ex2) { - if (isOnlyOneTruthy(ex1, ex2)) return false; +function isSameException(ex1, ex2) { + if (isOnlyOneTruthy(ex1, ex2)) return false; - ex1 = ex1.values[0]; - ex2 = ex2.values[0]; + ex1 = ex1.values[0]; + ex2 = ex2.values[0]; - if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false; + if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false; - return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); - } + return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); +} - /** +/** * Returns true if the two input stack trace interfaces have the same content */ - function isSameStacktrace(stack1, stack2) { - if (isOnlyOneTruthy(stack1, stack2)) return false; - - var frames1 = stack1.frames; - var frames2 = stack2.frames; - - // Exit early if frame count differs - if (frames1.length !== frames2.length) return false; - - // Iterate through every frame; bail out if anything differs - var a, b; - for (var i = 0; i < frames1.length; i++) { - a = frames1[i]; - b = frames2[i]; - if ( - a.filename !== b.filename || - a.lineno !== b.lineno || - a.colno !== b.colno || - a['function'] !== b['function'] - ) - return false; - } - return true; - } +function isSameStacktrace(stack1, stack2) { + if (isOnlyOneTruthy(stack1, stack2)) return false; + + var frames1 = stack1.frames; + var frames2 = stack2.frames; + + // Exit early if frame count differs + if (frames1.length !== frames2.length) return false; + + // Iterate through every frame; bail out if anything differs + var a, b; + for (var i = 0; i < frames1.length; i++) { + a = frames1[i]; + b = frames2[i]; + if ( + a.filename !== b.filename || + a.lineno !== b.lineno || + a.colno !== b.colno || + a['function'] !== b['function'] + ) + return false; + } + return true; +} - /** +/** * Polyfill a method * @param obj object e.g. `document` * @param name method name present on object e.g. `addEventListener` * @param replacement replacement function * @param track {optional} record instrumentation to an array */ - function fill(obj, name, replacement, track) { - var orig = obj[name]; - obj[name] = replacement(orig); - if (track) { - track.push([obj, name, orig]); - } - } - - if (typeof __DEV__ !== 'undefined' && __DEV__) { - Raven.utils = { - isUndefined: isUndefined, - isFunction: isFunction, - isString: isString, - isObject: isObject, - isEmptyObject: isEmptyObject, - isError: isError, - each: each, - objectMerge: objectMerge, - truncate: truncate, - hasKey: hasKey, - joinRegExp: joinRegExp, - urlencode: urlencode, - uuid4: uuid4, - htmlTreeAsString: htmlTreeAsString, - htmlElementAsString: htmlElementAsString, - parseUrl: parseUrl, - fill: fill - }; - } - - // Deprecations - Raven.prototype.setUser = Raven.prototype.setUserContext; - Raven.prototype.setReleaseContext = Raven.prototype.setRelease; - - module.exports = Raven; - }.call( - this, - typeof global !== 'undefined' - ? global - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' ? window : {} - )); - }, - {'1': 1, '2': 2, '5': 5, '6': 6, '7': 7} - ], - 4: [ - function(_dereq_, module, exports) { - (function(global) { - /** - * Enforces a single instance of the Raven client, and the - * main entry point for Raven. If you are a consumer of the - * Raven library, you SHOULD load this file (vs raven.js). - **/ - - var RavenConstructor = _dereq_(3); - - // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) - var _window = - typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : typeof self !== 'undefined' ? self : {}; - var _Raven = _window.Raven; - - var Raven = new RavenConstructor(); - - /* - * Allow multiple versions of Raven to be installed. - * Strip Raven from the global context and returns the instance. - * - * @return {Raven} - */ - Raven.noConflict = function() { - _window.Raven = _Raven; - return Raven; - }; - - Raven.afterLoad(); - - module.exports = Raven; - }.call( - this, - typeof global !== 'undefined' - ? global - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' ? window : {} - )); - }, - {'3': 3} - ], - 5: [ - function(_dereq_, module, exports) { - function isObject(what) { - return typeof what === 'object' && what !== null; - } - - // Yanked from https://git.io/vS8DV re-used under CC0 - // with some tiny modifications - function isError(value) { - switch ({}.toString.call(value)) { - case '[object Error]': - return true; - case '[object Exception]': - return true; - case '[object DOMException]': - return true; - default: - return value instanceof Error; - } - } - - function wrappedCallback(callback) { - function dataCallback(data, original) { - var normalizedData = callback(data) || data; - if (original) { - return original(normalizedData) || normalizedData; - } - return normalizedData; - } - - return dataCallback; - } +function fill(obj, name, replacement, track) { + var orig = obj[name]; + obj[name] = replacement(orig); + obj[name].__raven__ = true; + obj[name].__orig__ = orig; + if (track) { + track.push([obj, name, orig]); + } +} + +module.exports = { + isObject: isObject, + isError: isError, + isErrorEvent: isErrorEvent, + isUndefined: isUndefined, + isFunction: isFunction, + isString: isString, + isArray: isArray, + isEmptyObject: isEmptyObject, + supportsErrorEvent: supportsErrorEvent, + wrappedCallback: wrappedCallback, + each: each, + objectMerge: objectMerge, + truncate: truncate, + objectFrozen: objectFrozen, + hasKey: hasKey, + joinRegExp: joinRegExp, + urlencode: urlencode, + uuid4: uuid4, + htmlTreeAsString: htmlTreeAsString, + htmlElementAsString: htmlElementAsString, + isSameException: isSameException, + isSameStacktrace: isSameStacktrace, + parseUrl: parseUrl, + fill: fill +}; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],6:[function(_dereq_,module,exports){ +(function (global){ +var utils = _dereq_(5); - module.exports = { - isObject: isObject, - isError: isError, - wrappedCallback: wrappedCallback - }; - }, - {} - ], - 6: [ - function(_dereq_, module, exports) { - (function(global) { - var utils = _dereq_(5); - - /* +/* TraceKit - Cross brower stack traces This was originally forked from github.com/occ/TraceKit, but has since been @@ -2550,33 +2470,31 @@ MIT license */ - var TraceKit = { - collectWindowErrors: true, - debug: false - }; +var TraceKit = { + collectWindowErrors: true, + debug: false +}; - // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) - var _window = - typeof window !== 'undefined' - ? window - : typeof global !== 'undefined' - ? global - : typeof self !== 'undefined' ? self : {}; +// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) +var _window = + typeof window !== 'undefined' + ? window + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - // global reference to slice - var _slice = [].slice; - var UNKNOWN_FUNCTION = '?'; +// global reference to slice +var _slice = [].slice; +var UNKNOWN_FUNCTION = '?'; - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types - var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types +var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; - function getLocationHref() { - if (typeof document === 'undefined' || document.location == null) return ''; +function getLocationHref() { + if (typeof document === 'undefined' || document.location == null) return ''; - return document.location.href; - } + return document.location.href; +} - /** +/** * TraceKit.report: cross-browser processing of unhandled exceptions * * Syntax: @@ -2615,68 +2533,68 @@ * Handlers receive a stackInfo object as described in the * TraceKit.computeStackTrace docs. */ - TraceKit.report = (function reportModuleWrapper() { - var handlers = [], - lastArgs = null, - lastException = null, - lastExceptionStack = null; +TraceKit.report = (function reportModuleWrapper() { + var handlers = [], + lastArgs = null, + lastException = null, + lastExceptionStack = null; - /** + /** * Add a crash handler. * @param {Function} handler */ - function subscribe(handler) { - installGlobalHandler(); - handlers.push(handler); - } + function subscribe(handler) { + installGlobalHandler(); + handlers.push(handler); + } - /** + /** * Remove a crash handler. * @param {Function} handler */ - function unsubscribe(handler) { - for (var i = handlers.length - 1; i >= 0; --i) { - if (handlers[i] === handler) { - handlers.splice(i, 1); - } - } - } + function unsubscribe(handler) { + for (var i = handlers.length - 1; i >= 0; --i) { + if (handlers[i] === handler) { + handlers.splice(i, 1); + } + } + } - /** + /** * Remove all crash handlers. */ - function unsubscribeAll() { - uninstallGlobalHandler(); - handlers = []; - } + function unsubscribeAll() { + uninstallGlobalHandler(); + handlers = []; + } - /** + /** * Dispatch stack information to all handlers. * @param {Object.} stack */ - function notifyHandlers(stack, isWindowError) { - var exception = null; - if (isWindowError && !TraceKit.collectWindowErrors) { - return; - } - for (var i in handlers) { - if (handlers.hasOwnProperty(i)) { - try { - handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2))); - } catch (inner) { - exception = inner; - } - } - } + function notifyHandlers(stack, isWindowError) { + var exception = null; + if (isWindowError && !TraceKit.collectWindowErrors) { + return; + } + for (var i in handlers) { + if (handlers.hasOwnProperty(i)) { + try { + handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2))); + } catch (inner) { + exception = inner; + } + } + } - if (exception) { - throw exception; - } - } + if (exception) { + throw exception; + } + } - var _oldOnerrorHandler, _onErrorHandlerInstalled; + var _oldOnerrorHandler, _onErrorHandlerInstalled; - /** + /** * Ensures all global unhandled exceptions are recorded. * Supported by Gecko and IE. * @param {string} message Error message. @@ -2687,135 +2605,132 @@ * occurred. * @param {?Error} ex The actual Error object. */ - function traceKitWindowOnError(message, url, lineNo, colNo, ex) { - var stack = null; - - if (lastExceptionStack) { - TraceKit.computeStackTrace.augmentStackTraceWithInitialElement( - lastExceptionStack, - url, - lineNo, - message - ); - processLastException(); - } else if (ex && utils.isError(ex)) { - // non-string `ex` arg; attempt to extract stack trace - - // New chrome and blink send along a real error object - // Let's just report that like a normal error. - // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror - stack = TraceKit.computeStackTrace(ex); - notifyHandlers(stack, true); - } else { - var location = { - url: url, - line: lineNo, - column: colNo - }; - - var name = undefined; - var msg = message; // must be new var or will modify original `arguments` - var groups; - if ({}.toString.call(message) === '[object String]') { - var groups = message.match(ERROR_TYPES_RE); - if (groups) { - name = groups[1]; - msg = groups[2]; - } - } + function traceKitWindowOnError(message, url, lineNo, colNo, ex) { + var stack = null; + + if (lastExceptionStack) { + TraceKit.computeStackTrace.augmentStackTraceWithInitialElement( + lastExceptionStack, + url, + lineNo, + message + ); + processLastException(); + } else if (ex && utils.isError(ex)) { + // non-string `ex` arg; attempt to extract stack trace + + // New chrome and blink send along a real error object + // Let's just report that like a normal error. + // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror + stack = TraceKit.computeStackTrace(ex); + notifyHandlers(stack, true); + } else { + var location = { + url: url, + line: lineNo, + column: colNo + }; + + var name = undefined; + var msg = message; // must be new var or will modify original `arguments` + var groups; + if ({}.toString.call(message) === '[object String]') { + var groups = message.match(ERROR_TYPES_RE); + if (groups) { + name = groups[1]; + msg = groups[2]; + } + } - location.func = UNKNOWN_FUNCTION; + location.func = UNKNOWN_FUNCTION; - stack = { - name: name, - message: msg, - url: getLocationHref(), - stack: [location] - }; - notifyHandlers(stack, true); - } + stack = { + name: name, + message: msg, + url: getLocationHref(), + stack: [location] + }; + notifyHandlers(stack, true); + } - if (_oldOnerrorHandler) { - return _oldOnerrorHandler.apply(this, arguments); - } + if (_oldOnerrorHandler) { + return _oldOnerrorHandler.apply(this, arguments); + } - return false; - } + return false; + } - function installGlobalHandler() { - if (_onErrorHandlerInstalled) { - return; - } - _oldOnerrorHandler = _window.onerror; - _window.onerror = traceKitWindowOnError; - _onErrorHandlerInstalled = true; - } + function installGlobalHandler() { + if (_onErrorHandlerInstalled) { + return; + } + _oldOnerrorHandler = _window.onerror; + _window.onerror = traceKitWindowOnError; + _onErrorHandlerInstalled = true; + } - function uninstallGlobalHandler() { - if (!_onErrorHandlerInstalled) { - return; - } - _window.onerror = _oldOnerrorHandler; - _onErrorHandlerInstalled = false; - _oldOnerrorHandler = undefined; - } + function uninstallGlobalHandler() { + if (!_onErrorHandlerInstalled) { + return; + } + _window.onerror = _oldOnerrorHandler; + _onErrorHandlerInstalled = false; + _oldOnerrorHandler = undefined; + } - function processLastException() { - var _lastExceptionStack = lastExceptionStack, - _lastArgs = lastArgs; - lastArgs = null; - lastExceptionStack = null; - lastException = null; - notifyHandlers.apply( - null, - [_lastExceptionStack, false].concat(_lastArgs) - ); - } + function processLastException() { + var _lastExceptionStack = lastExceptionStack, + _lastArgs = lastArgs; + lastArgs = null; + lastExceptionStack = null; + lastException = null; + notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs)); + } - /** + /** * Reports an unhandled Error to TraceKit. * @param {Error} ex * @param {?boolean} rethrow If false, do not re-throw the exception. * Only used for window.onerror to not cause an infinite loop of * rethrowing. */ - function report(ex, rethrow) { - var args = _slice.call(arguments, 1); - if (lastExceptionStack) { - if (lastException === ex) { - return; // already caught by an inner catch block, ignore - } else { - processLastException(); - } - } + function report(ex, rethrow) { + var args = _slice.call(arguments, 1); + if (lastExceptionStack) { + if (lastException === ex) { + return; // already caught by an inner catch block, ignore + } else { + processLastException(); + } + } - var stack = TraceKit.computeStackTrace(ex); - lastExceptionStack = stack; - lastException = ex; - lastArgs = args; - - // If the stack trace is incomplete, wait for 2 seconds for - // slow slow IE to see if onerror occurs or not before reporting - // this exception; otherwise, we will end up with an incomplete - // stack trace - setTimeout(function() { - if (lastException === ex) { - processLastException(); - } - }, stack.incomplete ? 2000 : 0); + var stack = TraceKit.computeStackTrace(ex); + lastExceptionStack = stack; + lastException = ex; + lastArgs = args; + + // If the stack trace is incomplete, wait for 2 seconds for + // slow slow IE to see if onerror occurs or not before reporting + // this exception; otherwise, we will end up with an incomplete + // stack trace + setTimeout(function() { + if (lastException === ex) { + processLastException(); + } + }, stack.incomplete ? 2000 : 0); - if (rethrow !== false) { - throw ex; // re-throw to propagate to the top level (and cause window.onerror) - } - } + if (rethrow !== false) { + throw ex; // re-throw to propagate to the top level (and cause window.onerror) + } + } - report.subscribe = subscribe; - report.unsubscribe = unsubscribe; - report.uninstall = unsubscribeAll; - return report; - })(); + report.subscribe = subscribe; + report.unsubscribe = unsubscribe; + report.uninstall = unsubscribeAll; + return report; +})(); - /** +/** * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript * * Syntax: @@ -2866,140 +2781,136 @@ * inner function that actually caused the exception). * */ - TraceKit.computeStackTrace = (function computeStackTraceWrapper() { - // Contents of Exception in various browsers. - // - // SAFARI: - // ex.message = Can't find variable: qq - // ex.line = 59 - // ex.sourceId = 580238192 - // ex.sourceURL = http://... - // ex.expressionBeginOffset = 96 - // ex.expressionCaretOffset = 98 - // ex.expressionEndOffset = 98 - // ex.name = ReferenceError - // - // FIREFOX: - // ex.message = qq is not defined - // ex.fileName = http://... - // ex.lineNumber = 59 - // ex.columnNumber = 69 - // ex.stack = ...stack trace... (see the example below) - // ex.name = ReferenceError - // - // CHROME: - // ex.message = qq is not defined - // ex.name = ReferenceError - // ex.type = not_defined - // ex.arguments = ['aa'] - // ex.stack = ...stack trace... - // - // INTERNET EXPLORER: - // ex.message = ... - // ex.name = ReferenceError - // - // OPERA: - // ex.message = ...message... (see the example below) - // ex.name = ReferenceError - // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message) - // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace' - - /** +TraceKit.computeStackTrace = (function computeStackTraceWrapper() { + // Contents of Exception in various browsers. + // + // SAFARI: + // ex.message = Can't find variable: qq + // ex.line = 59 + // ex.sourceId = 580238192 + // ex.sourceURL = http://... + // ex.expressionBeginOffset = 96 + // ex.expressionCaretOffset = 98 + // ex.expressionEndOffset = 98 + // ex.name = ReferenceError + // + // FIREFOX: + // ex.message = qq is not defined + // ex.fileName = http://... + // ex.lineNumber = 59 + // ex.columnNumber = 69 + // ex.stack = ...stack trace... (see the example below) + // ex.name = ReferenceError + // + // CHROME: + // ex.message = qq is not defined + // ex.name = ReferenceError + // ex.type = not_defined + // ex.arguments = ['aa'] + // ex.stack = ...stack trace... + // + // INTERNET EXPLORER: + // ex.message = ... + // ex.name = ReferenceError + // + // OPERA: + // ex.message = ...message... (see the example below) + // ex.name = ReferenceError + // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message) + // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace' + + /** * Computes stack trace information from the stack property. * Chrome and Gecko use this property. * @param {Error} ex * @return {?Object.} Stack trace information. */ - function computeStackTraceFromStackProp(ex) { - if (typeof ex.stack === 'undefined' || !ex.stack) return; - - var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, - gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, - winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, - // Used to additionally parse URL/line/column from eval frames - geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, - chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/, - lines = ex.stack.split('\n'), - stack = [], - submatch, - parts, - element, - reference = /^(.*) is undefined$/.exec(ex.message); - - for (var i = 0, j = lines.length; i < j; ++i) { - if ((parts = chrome.exec(lines[i]))) { - var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line - var isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line - if (isEval && (submatch = chromeEval.exec(parts[2]))) { - // throw out eval line/column and use top-most line/column number - parts[2] = submatch[1]; // url - parts[3] = submatch[2]; // line - parts[4] = submatch[3]; // column - } - element = { - url: !isNative ? parts[2] : null, - func: parts[1] || UNKNOWN_FUNCTION, - args: isNative ? [parts[2]] : [], - line: parts[3] ? +parts[3] : null, - column: parts[4] ? +parts[4] : null - }; - } else if ((parts = winjs.exec(lines[i]))) { - element = { - url: parts[2], - func: parts[1] || UNKNOWN_FUNCTION, - args: [], - line: +parts[3], - column: parts[4] ? +parts[4] : null - }; - } else if ((parts = gecko.exec(lines[i]))) { - var isEval = parts[3] && parts[3].indexOf(' > eval') > -1; - if (isEval && (submatch = geckoEval.exec(parts[3]))) { - // throw out eval line/column and use top-most line number - parts[3] = submatch[1]; - parts[4] = submatch[2]; - parts[5] = null; // no column when eval - } else if ( - i === 0 && - !parts[5] && - typeof ex.columnNumber !== 'undefined' - ) { - // FireFox uses this awesome columnNumber property for its top frame - // Also note, Firefox's column number is 0-based and everything else expects 1-based, - // so adding 1 - // NOTE: this hack doesn't work if top-most frame is eval - stack[0].column = ex.columnNumber + 1; - } - element = { - url: parts[3], - func: parts[1] || UNKNOWN_FUNCTION, - args: parts[2] ? parts[2].split(',') : [], - line: parts[4] ? +parts[4] : null, - column: parts[5] ? +parts[5] : null - }; - } else { - continue; - } + function computeStackTraceFromStackProp(ex) { + if (typeof ex.stack === 'undefined' || !ex.stack) return; + + var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, + gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, + winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + // Used to additionally parse URL/line/column from eval frames + geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, + chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/, + lines = ex.stack.split('\n'), + stack = [], + submatch, + parts, + element, + reference = /^(.*) is undefined$/.exec(ex.message); + + for (var i = 0, j = lines.length; i < j; ++i) { + if ((parts = chrome.exec(lines[i]))) { + var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line + var isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line + if (isEval && (submatch = chromeEval.exec(parts[2]))) { + // throw out eval line/column and use top-most line/column number + parts[2] = submatch[1]; // url + parts[3] = submatch[2]; // line + parts[4] = submatch[3]; // column + } + element = { + url: !isNative ? parts[2] : null, + func: parts[1] || UNKNOWN_FUNCTION, + args: isNative ? [parts[2]] : [], + line: parts[3] ? +parts[3] : null, + column: parts[4] ? +parts[4] : null + }; + } else if ((parts = winjs.exec(lines[i]))) { + element = { + url: parts[2], + func: parts[1] || UNKNOWN_FUNCTION, + args: [], + line: +parts[3], + column: parts[4] ? +parts[4] : null + }; + } else if ((parts = gecko.exec(lines[i]))) { + var isEval = parts[3] && parts[3].indexOf(' > eval') > -1; + if (isEval && (submatch = geckoEval.exec(parts[3]))) { + // throw out eval line/column and use top-most line number + parts[3] = submatch[1]; + parts[4] = submatch[2]; + parts[5] = null; // no column when eval + } else if (i === 0 && !parts[5] && typeof ex.columnNumber !== 'undefined') { + // FireFox uses this awesome columnNumber property for its top frame + // Also note, Firefox's column number is 0-based and everything else expects 1-based, + // so adding 1 + // NOTE: this hack doesn't work if top-most frame is eval + stack[0].column = ex.columnNumber + 1; + } + element = { + url: parts[3], + func: parts[1] || UNKNOWN_FUNCTION, + args: parts[2] ? parts[2].split(',') : [], + line: parts[4] ? +parts[4] : null, + column: parts[5] ? +parts[5] : null + }; + } else { + continue; + } - if (!element.func && element.line) { - element.func = UNKNOWN_FUNCTION; - } + if (!element.func && element.line) { + element.func = UNKNOWN_FUNCTION; + } - stack.push(element); - } + stack.push(element); + } - if (!stack.length) { - return null; - } + if (!stack.length) { + return null; + } - return { - name: ex.name, - message: ex.message, - url: getLocationHref(), - stack: stack - }; - } + return { + name: ex.name, + message: ex.message, + url: getLocationHref(), + stack: stack + }; + } - /** + /** * Adds information about the first frame to incomplete stack traces. * Safari and IE require this to get complete data on the first frame. * @param {Object.} stackInfo Stack trace information from @@ -3012,49 +2923,44 @@ * @return {boolean} Whether or not the stack information was * augmented. */ - function augmentStackTraceWithInitialElement( - stackInfo, - url, - lineNo, - message - ) { - var initial = { - url: url, - line: lineNo - }; + function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) { + var initial = { + url: url, + line: lineNo + }; - if (initial.url && initial.line) { - stackInfo.incomplete = false; + if (initial.url && initial.line) { + stackInfo.incomplete = false; - if (!initial.func) { - initial.func = UNKNOWN_FUNCTION; - } + if (!initial.func) { + initial.func = UNKNOWN_FUNCTION; + } - if (stackInfo.stack.length > 0) { - if (stackInfo.stack[0].url === initial.url) { - if (stackInfo.stack[0].line === initial.line) { - return false; // already in stack trace - } else if ( - !stackInfo.stack[0].line && - stackInfo.stack[0].func === initial.func - ) { - stackInfo.stack[0].line = initial.line; - return false; - } - } - } + if (stackInfo.stack.length > 0) { + if (stackInfo.stack[0].url === initial.url) { + if (stackInfo.stack[0].line === initial.line) { + return false; // already in stack trace + } else if ( + !stackInfo.stack[0].line && + stackInfo.stack[0].func === initial.func + ) { + stackInfo.stack[0].line = initial.line; + return false; + } + } + } - stackInfo.stack.unshift(initial); - stackInfo.partial = true; - return true; - } else { - stackInfo.incomplete = true; - } + stackInfo.stack.unshift(initial); + stackInfo.partial = true; + return true; + } else { + stackInfo.incomplete = true; + } - return false; - } + return false; + } - /** + /** * Computes stack trace information by walking the arguments.caller * chain at the time the exception occurred. This will cause earlier * frames to be missed but is the only way to get any stack trace in @@ -3063,132 +2969,122 @@ * @param {Error} ex * @return {?Object.} Stack trace information. */ - function computeStackTraceByWalkingCallerChain(ex, depth) { - var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i, - stack = [], - funcs = {}, - recursion = false, - parts, - item, - source; - - for ( - var curr = computeStackTraceByWalkingCallerChain.caller; - curr && !recursion; - curr = curr.caller - ) { - if (curr === computeStackTrace || curr === TraceKit.report) { - // console.log('skipping internal function'); - continue; - } + function computeStackTraceByWalkingCallerChain(ex, depth) { + var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i, + stack = [], + funcs = {}, + recursion = false, + parts, + item, + source; + + for ( + var curr = computeStackTraceByWalkingCallerChain.caller; + curr && !recursion; + curr = curr.caller + ) { + if (curr === computeStackTrace || curr === TraceKit.report) { + // console.log('skipping internal function'); + continue; + } - item = { - url: null, - func: UNKNOWN_FUNCTION, - line: null, - column: null - }; - - if (curr.name) { - item.func = curr.name; - } else if ((parts = functionName.exec(curr.toString()))) { - item.func = parts[1]; - } + item = { + url: null, + func: UNKNOWN_FUNCTION, + line: null, + column: null + }; + + if (curr.name) { + item.func = curr.name; + } else if ((parts = functionName.exec(curr.toString()))) { + item.func = parts[1]; + } - if (typeof item.func === 'undefined') { - try { - item.func = parts.input.substring(0, parts.input.indexOf('{')); - } catch (e) {} - } + if (typeof item.func === 'undefined') { + try { + item.func = parts.input.substring(0, parts.input.indexOf('{')); + } catch (e) {} + } - if (funcs['' + curr]) { - recursion = true; - } else { - funcs['' + curr] = true; - } + if (funcs['' + curr]) { + recursion = true; + } else { + funcs['' + curr] = true; + } - stack.push(item); - } + stack.push(item); + } - if (depth) { - // console.log('depth is ' + depth); - // console.log('stack is ' + stack.length); - stack.splice(0, depth); - } + if (depth) { + // console.log('depth is ' + depth); + // console.log('stack is ' + stack.length); + stack.splice(0, depth); + } - var result = { - name: ex.name, - message: ex.message, - url: getLocationHref(), - stack: stack - }; - augmentStackTraceWithInitialElement( - result, - ex.sourceURL || ex.fileName, - ex.line || ex.lineNumber, - ex.message || ex.description - ); - return result; - } + var result = { + name: ex.name, + message: ex.message, + url: getLocationHref(), + stack: stack + }; + augmentStackTraceWithInitialElement( + result, + ex.sourceURL || ex.fileName, + ex.line || ex.lineNumber, + ex.message || ex.description + ); + return result; + } - /** + /** * Computes a stack trace for an exception. * @param {Error} ex * @param {(string|number)=} depth */ - function computeStackTrace(ex, depth) { - var stack = null; - depth = depth == null ? 0 : +depth; + function computeStackTrace(ex, depth) { + var stack = null; + depth = depth == null ? 0 : +depth; + + try { + stack = computeStackTraceFromStackProp(ex); + if (stack) { + return stack; + } + } catch (e) { + if (TraceKit.debug) { + throw e; + } + } - try { - stack = computeStackTraceFromStackProp(ex); - if (stack) { - return stack; - } - } catch (e) { - if (TraceKit.debug) { - throw e; - } - } + try { + stack = computeStackTraceByWalkingCallerChain(ex, depth + 1); + if (stack) { + return stack; + } + } catch (e) { + if (TraceKit.debug) { + throw e; + } + } + return { + name: ex.name, + message: ex.message, + url: getLocationHref() + }; + } - try { - stack = computeStackTraceByWalkingCallerChain(ex, depth + 1); - if (stack) { - return stack; - } - } catch (e) { - if (TraceKit.debug) { - throw e; - } - } - return { - name: ex.name, - message: ex.message, - url: getLocationHref() - }; - } + computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; + computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp; - computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; - computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp; - - return computeStackTrace; - })(); - - module.exports = TraceKit; - }.call( - this, - typeof global !== 'undefined' - ? global - : typeof self !== 'undefined' - ? self - : typeof window !== 'undefined' ? window : {} - )); - }, - {'5': 5} - ], - 7: [ - function(_dereq_, module, exports) { - /* + return computeStackTrace; +})(); + +module.exports = TraceKit; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"5":5}],7:[function(_dereq_,module,exports){ +/* json-stringify-safe Like JSON.stringify, but doesn't throw on circular references. @@ -3199,76 +3095,69 @@ ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE */ - exports = module.exports = stringify; - exports.getSerialize = serializer; - - function indexOf(haystack, needle) { - for (var i = 0; i < haystack.length; ++i) { - if (haystack[i] === needle) return i; - } - return -1; - } - - function stringify(obj, replacer, spaces, cycleReplacer) { - return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces); - } +exports = module.exports = stringify; +exports.getSerialize = serializer; - // https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106 - function stringifyError(value) { - var err = { - // These properties are implemented as magical getters and don't show up in for in - stack: value.stack, - message: value.message, - name: value.name - }; +function indexOf(haystack, needle) { + for (var i = 0; i < haystack.length; ++i) { + if (haystack[i] === needle) return i; + } + return -1; +} + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces); +} + +// https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106 +function stringifyError(value) { + var err = { + // These properties are implemented as magical getters and don't show up in for in + stack: value.stack, + message: value.message, + name: value.name + }; + + for (var i in value) { + if (Object.prototype.hasOwnProperty.call(value, i)) { + err[i] = value[i]; + } + } - for (var i in value) { - if (Object.prototype.hasOwnProperty.call(value, i)) { - err[i] = value[i]; - } - } + return err; +} - return err; - } +function serializer(replacer, cycleReplacer) { + var stack = []; + var keys = []; - function serializer(replacer, cycleReplacer) { - var stack = []; - var keys = []; + if (cycleReplacer == null) { + cycleReplacer = function(key, value) { + if (stack[0] === value) { + return '[Circular ~]'; + } + return '[Circular ~.' + keys.slice(0, indexOf(stack, value)).join('.') + ']'; + }; + } - if (cycleReplacer == null) { - cycleReplacer = function(key, value) { - if (stack[0] === value) { - return '[Circular ~]'; - } - return ( - '[Circular ~.' + keys.slice(0, indexOf(stack, value)).join('.') + ']' - ); - }; - } + return function(key, value) { + if (stack.length > 0) { + var thisPos = indexOf(stack, this); + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this); + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); - return function(key, value) { - if (stack.length > 0) { - var thisPos = indexOf(stack, this); - ~thisPos ? stack.splice(thisPos + 1) : stack.push(this); - ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); + if (~indexOf(stack, value)) { + value = cycleReplacer.call(this, key, value); + } + } else { + stack.push(value); + } - if (~indexOf(stack, value)) { - value = cycleReplacer.call(this, key, value); - } - } else { - stack.push(value); - } + return replacer == null + ? value instanceof Error ? stringifyError(value) : value + : replacer.call(this, key, value); + }; +} - return replacer == null - ? value instanceof Error ? stringifyError(value) : value - : replacer.call(this, key, value); - }; - } - }, - {} - ] - }, - {}, - [4] - )(4); -}); +},{}]},{},[4])(4) +}); \ No newline at end of file