Bug 1454029 - Export Screenshots 32.1.0 to Firefox (upgrade Raven to 3.24.1); r?_6a68 draft
authorIan Bicking <ianb@colorstudy.com>
Thu, 19 Apr 2018 16:09:58 -0500
changeset 786776 bcddc177cf7c2720a321f3156616774a4c058038
parent 786775 f17590d444f381d60d13eb2d7658ca5a4c6a97b8
child 786777 ab795e1b37e5a32e08e0890d5af603b4ec33f8ef
child 786778 d3fe00d013421fe1b8e88a48c2e0fd172cd96842
push id107569
push userbmo:ianb@mozilla.com
push dateMon, 23 Apr 2018 20:37:27 +0000
reviewers_6a68
bugs1454029
milestone61.0a1
Bug 1454029 - Export Screenshots 32.1.0 to Firefox (upgrade Raven to 3.24.1); r?_6a68 MozReview-Commit-ID: 1cYxWWwLzmQ
browser/extensions/screenshots/webextension/build/raven.js
--- a/browser/extensions/screenshots/webextension/build/raven.js
+++ b/browser/extensions/screenshots/webextension/build/raven.js
@@ -1,2587 +1,2942 @@
-/*! Raven.js 3.18.1 (2dca364) | github.com/getsentry/raven-js */
+/*! Raven.js 3.24.1 (f3b3500) | github.com/getsentry/raven-js */
 
 /*
  * Includes TraceKit
  * https://github.com/getsentry/TraceKit
  *
- * Copyright 2017 Matt Robenolt and other contributors
+ * Copyright 2018 Matt Robenolt and other contributors
  * Released under the BSD license
  * https://github.com/getsentry/raven-js/blob/master/LICENSE
  *
  */
 
-(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(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 utils = _dereq_(5);
+
+var wrapMethod = function(console, level, callback) {
+  var originalConsoleLevel = console[level];
+  var originalConsole = console;
+
+  if (!(level in console)) {
+    return;
   }
-})(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
-        );
+
+  var sentryLevel = level === 'warn' ? 'warning' : level;
+
+  console[level] = function() {
+    var args = [].slice.call(arguments);
+
+    var msg = utils.safeJoin(args, ' ');
+    var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}};
+
+    if (level === 'assert') {
+      if (args[0] === false) {
+        // Default browsers message
+        msg =
+          'Assertion failed: ' + (utils.safeJoin(args.slice(1), ' ') || 'console.assert');
+        data.extra.arguments = args.slice(1);
+        callback && callback(msg, data);
       }
-      return n[o].exports;
+    } else {
+      callback && callback(msg, data);
+    }
+
+    // this fails for some browsers. :(
+    if (originalConsoleLevel) {
+      // IE9 doesn't allow calling apply on console functions directly
+      // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
+      Function.prototype.apply.call(originalConsoleLevel, originalConsole, args);
     }
-    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;
-            }
-
-            var sentryLevel = level === 'warn' ? 'warning' : level;
-
-            console[level] = function() {
-              var args = [].slice.call(arguments);
-
-              var msg = '' + args.join(' ');
-              var data = {
-                level: sentryLevel,
-                logger: 'console',
-                extra: {arguments: args}
-              };
-
-              if (level === 'assert') {
-                if (args[0] === false) {
-                  // Default browsers message
-                  msg =
-                    'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert');
-                  data.extra.arguments = args.slice(1);
-                  callback && callback(msg, data);
-                }
-              } else {
-                callback && callback(msg, data);
-              }
-
-              // this fails for some browsers. :(
-              if (originalConsoleLevel) {
-                // IE9 doesn't allow calling apply on console functions directly
-                // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193
-                Function.prototype.apply.call(
-                  originalConsoleLevel,
-                  originalConsole,
-                  args
-                );
-              }
-            };
-          };
-
-          module.exports = {
-            wrapMethod: wrapMethod
-          };
-        },
-        {}
-      ],
-      3: [
-        function(_dereq_, module, exports) {
-          (function(global) {
-            /*global XDomainRequest:false, __DEV__:false*/
-
-            var TraceKit = _dereq_(6);
-            var stringify = _dereq_(7);
-            var RavenConfigError = _dereq_(1);
-            var utils = _dereq_(5);
-
-            var isError = utils.isError,
-              isObject = utils.isObject;
-
-            var wrapConsoleMethod = _dereq_(2).wrapMethod;
-
-            var dsnKeys = 'source protocol user pass host port path'.split(' '),
-              dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
-
-            function now() {
-              return +new Date();
-            }
-
-            // 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 _document = _window.document;
-            var _navigator = _window.navigator;
-
-            function keepOriginalCallback(original, callback) {
-              return isFunction(callback)
-                ? function(data) {
-                    return callback(data, original);
-                  }
-                : callback;
-            }
-
-            // First, check for JSON support
-            // If there is no JSON, we no-op the core features of Raven
-            // since JSON is required to encode the payload
-            function Raven() {
-              this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
-              // Raven can run in contexts where there's no document (react-native)
-              this._hasDocument = !isUndefined(_document);
-              this._hasNavigator = !isUndefined(_navigator);
-              this._lastCapturedException = null;
-              this._lastData = null;
-              this._lastEventId = null;
-              this._globalServer = null;
-              this._globalKey = null;
-              this._globalProject = null;
-              this._globalContext = {};
-              this._globalOptions = {
-                logger: 'javascript',
-                ignoreErrors: [],
-                ignoreUrls: [],
-                whitelistUrls: [],
-                includePaths: [],
-                collectWindowErrors: true,
-                maxMessageLength: 0,
-
-                // By default, truncates URL values to 250 chars
-                maxUrlLength: 250,
-                stackTraceLimit: 50,
-                autoBreadcrumbs: true,
-                instrument: true,
-                sampleRate: 1
-              };
-              this._ignoreOnError = 0;
-              this._isRavenInstalled = false;
-              this._originalErrorStackTraceLimit = Error.stackTraceLimit;
-              // capture references to window.console *and* all its methods first
-              // before the console plugin has a chance to monkey patch
-              this._originalConsole = _window.console || {};
-              this._originalConsoleMethods = {};
-              this._plugins = [];
-              this._startTime = now();
-              this._wrappedBuiltIns = [];
-              this._breadcrumbs = [];
-              this._lastCapturedEvent = null;
-              this._keypressTimeout;
-              this._location = _window.location;
-              this._lastHref = this._location && this._location.href;
-              this._resetBackoff();
-
-              // eslint-disable-next-line guard-for-in
-              for (var method in this._originalConsole) {
-                this._originalConsoleMethods[method] = this._originalConsole[method];
-              }
-            }
-
-            /*
+  };
+};
+
+module.exports = {
+  wrapMethod: wrapMethod
+};
+
+},{"5":5}],3:[function(_dereq_,module,exports){
+(function (global){
+/*global XDomainRequest:false */
+
+var TraceKit = _dereq_(6);
+var stringify = _dereq_(7);
+var md5 = _dereq_(8);
+var RavenConfigError = _dereq_(1);
+
+var utils = _dereq_(5);
+var isError = utils.isError;
+var isObject = utils.isObject;
+var isPlainObject = utils.isPlainObject;
+var isErrorEvent = utils.isErrorEvent;
+var isUndefined = utils.isUndefined;
+var isFunction = utils.isFunction;
+var isString = utils.isString;
+var isArray = utils.isArray;
+var isEmptyObject = utils.isEmptyObject;
+var each = utils.each;
+var objectMerge = utils.objectMerge;
+var truncate = utils.truncate;
+var objectFrozen = utils.objectFrozen;
+var hasKey = utils.hasKey;
+var joinRegExp = utils.joinRegExp;
+var urlencode = utils.urlencode;
+var uuid4 = utils.uuid4;
+var htmlTreeAsString = utils.htmlTreeAsString;
+var isSameException = utils.isSameException;
+var isSameStacktrace = utils.isSameStacktrace;
+var parseUrl = utils.parseUrl;
+var fill = utils.fill;
+var supportsFetch = utils.supportsFetch;
+var supportsReferrerPolicy = utils.supportsReferrerPolicy;
+var serializeKeysForMessage = utils.serializeKeysForMessage;
+var serializeException = utils.serializeException;
+var sanitize = utils.sanitize;
+
+var wrapConsoleMethod = _dereq_(2).wrapMethod;
+
+var dsnKeys = 'source protocol user pass host port path'.split(' '),
+  dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
+
+function now() {
+  return +new Date();
+}
+
+// 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 _document = _window.document;
+var _navigator = _window.navigator;
+
+function keepOriginalCallback(original, callback) {
+  return isFunction(callback)
+    ? function(data) {
+        return callback(data, original);
+      }
+    : callback;
+}
+
+// First, check for JSON support
+// If there is no JSON, we no-op the core features of Raven
+// since JSON is required to encode the payload
+function Raven() {
+  this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify);
+  // Raven can run in contexts where there's no document (react-native)
+  this._hasDocument = !isUndefined(_document);
+  this._hasNavigator = !isUndefined(_navigator);
+  this._lastCapturedException = null;
+  this._lastData = null;
+  this._lastEventId = null;
+  this._globalServer = null;
+  this._globalKey = null;
+  this._globalProject = null;
+  this._globalContext = {};
+  this._globalOptions = {
+    // SENTRY_RELEASE can be injected by https://github.com/getsentry/sentry-webpack-plugin
+    release: _window.SENTRY_RELEASE && _window.SENTRY_RELEASE.id,
+    logger: 'javascript',
+    ignoreErrors: [],
+    ignoreUrls: [],
+    whitelistUrls: [],
+    includePaths: [],
+    headers: null,
+    collectWindowErrors: true,
+    captureUnhandledRejections: true,
+    maxMessageLength: 0,
+    // By default, truncates URL values to 250 chars
+    maxUrlLength: 250,
+    stackTraceLimit: 50,
+    autoBreadcrumbs: true,
+    instrument: true,
+    sampleRate: 1,
+    sanitizeKeys: []
+  };
+  this._fetchDefaults = {
+    method: 'POST',
+    keepalive: true,
+    // Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
+    // https://caniuse.com/#feat=referrer-policy
+    // It doesn't. And it throw exception instead of ignoring this parameter...
+    // REF: https://github.com/getsentry/raven-js/issues/1233
+    referrerPolicy: supportsReferrerPolicy() ? 'origin' : ''
+  };
+  this._ignoreOnError = 0;
+  this._isRavenInstalled = false;
+  this._originalErrorStackTraceLimit = Error.stackTraceLimit;
+  // capture references to window.console *and* all its methods first
+  // before the console plugin has a chance to monkey patch
+  this._originalConsole = _window.console || {};
+  this._originalConsoleMethods = {};
+  this._plugins = [];
+  this._startTime = now();
+  this._wrappedBuiltIns = [];
+  this._breadcrumbs = [];
+  this._lastCapturedEvent = null;
+  this._keypressTimeout;
+  this._location = _window.location;
+  this._lastHref = this._location && this._location.href;
+  this._resetBackoff();
+
+  // eslint-disable-next-line guard-for-in
+  for (var method in this._originalConsole) {
+    this._originalConsoleMethods[method] = this._originalConsole[method];
+  }
+}
+
+/*
  * The core Raven singleton
  *
  * @this {Raven}
  */
 
-            Raven.prototype = {
-              // Hardcode version string so that raven source can be loaded directly via
-              // webpack (using a build step causes webpack #1617). Grunt verifies that
-              // this value matches package.json during build.
-              //   See: https://github.com/getsentry/raven-js/issues/465
-              VERSION: '3.18.1',
-
-              debug: false,
-
-              TraceKit: TraceKit, // alias to TraceKit
-
-              /*
+Raven.prototype = {
+  // Hardcode version string so that raven source can be loaded directly via
+  // webpack (using a build step causes webpack #1617). Grunt verifies that
+  // this value matches package.json during build.
+  //   See: https://github.com/getsentry/raven-js/issues/465
+  VERSION: '3.24.1',
+
+  debug: false,
+
+  TraceKit: TraceKit, // alias to TraceKit
+
+  /*
      * Configure Raven with a DSN and extra options
      *
      * @param {string} dsn The public Sentry DSN
      * @param {object} options Set of global options [optional]
      * @return {Raven}
      */
-              config: function(dsn, options) {
-                var self = this;
-
-                if (self._globalServer) {
-                  this._logDebug('error', 'Error: Raven has already been configured');
-                  return self;
-                }
-                if (!dsn) return self;
-
-                var globalOptions = self._globalOptions;
-
-                // merge in options
-                if (options) {
-                  each(options, function(key, value) {
-                    // tags and extra are special and need to be put into context
-                    if (key === 'tags' || key === 'extra' || key === 'user') {
-                      self._globalContext[key] = value;
-                    } else {
-                      globalOptions[key] = value;
-                    }
-                  });
-                }
-
-                self.setDSN(dsn);
-
-                // "Script error." is hard coded into browsers for errors that it can't read.
-                // this is the result of a script being pulled in from an external domain and CORS.
-                globalOptions.ignoreErrors.push(/^Script error\.?$/);
-                globalOptions.ignoreErrors.push(
-                  /^Javascript error: Script error\.? on line 0$/
-                );
-
-                // join regexp rules into one big rule
-                globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
-                globalOptions.ignoreUrls = globalOptions.ignoreUrls.length
-                  ? joinRegExp(globalOptions.ignoreUrls)
-                  : false;
-                globalOptions.whitelistUrls = globalOptions.whitelistUrls.length
-                  ? joinRegExp(globalOptions.whitelistUrls)
-                  : false;
-                globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
-                globalOptions.maxBreadcrumbs = Math.max(
-                  0,
-                  Math.min(globalOptions.maxBreadcrumbs || 100, 100)
-                ); // default and hard limit is 100
-
-                var autoBreadcrumbDefaults = {
-                  xhr: true,
-                  console: true,
-                  dom: true,
-                  location: true
-                };
-
-                var autoBreadcrumbs = globalOptions.autoBreadcrumbs;
-                if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {
-                  autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);
-                } else if (autoBreadcrumbs !== false) {
-                  autoBreadcrumbs = autoBreadcrumbDefaults;
-                }
-                globalOptions.autoBreadcrumbs = autoBreadcrumbs;
-
-                var instrumentDefaults = {
-                  tryCatch: true
-                };
-
-                var instrument = globalOptions.instrument;
-                if ({}.toString.call(instrument) === '[object Object]') {
-                  instrument = objectMerge(instrumentDefaults, instrument);
-                } else if (instrument !== false) {
-                  instrument = instrumentDefaults;
-                }
-                globalOptions.instrument = instrument;
-
-                TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
-
-                // return for chaining
-                return self;
-              },
-
-              /*
+  config: function(dsn, options) {
+    var self = this;
+
+    if (self._globalServer) {
+      this._logDebug('error', 'Error: Raven has already been configured');
+      return self;
+    }
+    if (!dsn) return self;
+
+    var globalOptions = self._globalOptions;
+
+    // merge in options
+    if (options) {
+      each(options, function(key, value) {
+        // tags and extra are special and need to be put into context
+        if (key === 'tags' || key === 'extra' || key === 'user') {
+          self._globalContext[key] = value;
+        } else {
+          globalOptions[key] = value;
+        }
+      });
+    }
+
+    self.setDSN(dsn);
+
+    // "Script error." is hard coded into browsers for errors that it can't read.
+    // this is the result of a script being pulled in from an external domain and CORS.
+    globalOptions.ignoreErrors.push(/^Script error\.?$/);
+    globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/);
+
+    // join regexp rules into one big rule
+    globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors);
+    globalOptions.ignoreUrls = globalOptions.ignoreUrls.length
+      ? joinRegExp(globalOptions.ignoreUrls)
+      : false;
+    globalOptions.whitelistUrls = globalOptions.whitelistUrls.length
+      ? joinRegExp(globalOptions.whitelistUrls)
+      : false;
+    globalOptions.includePaths = joinRegExp(globalOptions.includePaths);
+    globalOptions.maxBreadcrumbs = Math.max(
+      0,
+      Math.min(globalOptions.maxBreadcrumbs || 100, 100)
+    ); // default and hard limit is 100
+
+    var autoBreadcrumbDefaults = {
+      xhr: true,
+      console: true,
+      dom: true,
+      location: true,
+      sentry: true
+    };
+
+    var autoBreadcrumbs = globalOptions.autoBreadcrumbs;
+    if ({}.toString.call(autoBreadcrumbs) === '[object Object]') {
+      autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs);
+    } else if (autoBreadcrumbs !== false) {
+      autoBreadcrumbs = autoBreadcrumbDefaults;
+    }
+    globalOptions.autoBreadcrumbs = autoBreadcrumbs;
+
+    var instrumentDefaults = {
+      tryCatch: true
+    };
+
+    var instrument = globalOptions.instrument;
+    if ({}.toString.call(instrument) === '[object Object]') {
+      instrument = objectMerge(instrumentDefaults, instrument);
+    } else if (instrument !== false) {
+      instrument = instrumentDefaults;
+    }
+    globalOptions.instrument = instrument;
+
+    TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
+
+    // return for chaining
+    return self;
+  },
+
+  /*
      * Installs a global window.onerror error handler
      * to capture and report uncaught exceptions.
      * At this point, install() is required to be called due
      * to the way TraceKit is set up.
      *
      * @return {Raven}
      */
-              install: function() {
-                var self = this;
-                if (self.isSetup() && !self._isRavenInstalled) {
-                  TraceKit.report.subscribe(function() {
-                    self._handleOnErrorStackInfo.apply(self, arguments);
-                  });
-                  if (
-                    self._globalOptions.instrument &&
-                    self._globalOptions.instrument.tryCatch
-                  ) {
-                    self._instrumentTryCatch();
-                  }
-
-                  if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs();
-
-                  // Install all of the plugins
-                  self._drainPlugins();
-
-                  self._isRavenInstalled = true;
-                }
-
-                Error.stackTraceLimit = self._globalOptions.stackTraceLimit;
-                return this;
-              },
-
-              /*
+  install: function() {
+    var self = this;
+    if (self.isSetup() && !self._isRavenInstalled) {
+      TraceKit.report.subscribe(function() {
+        self._handleOnErrorStackInfo.apply(self, arguments);
+      });
+
+      if (self._globalOptions.captureUnhandledRejections) {
+        self._attachPromiseRejectionHandler();
+      }
+
+      self._patchFunctionToString();
+
+      if (self._globalOptions.instrument && self._globalOptions.instrument.tryCatch) {
+        self._instrumentTryCatch();
+      }
+
+      if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs();
+
+      // Install all of the plugins
+      self._drainPlugins();
+
+      self._isRavenInstalled = true;
+    }
+
+    Error.stackTraceLimit = self._globalOptions.stackTraceLimit;
+    return this;
+  },
+
+  /*
      * Set the DSN (can be called multiple time unlike config)
      *
      * @param {string} dsn The public Sentry DSN
      */
-              setDSN: function(dsn) {
-                var self = this,
-                  uri = self._parseDSN(dsn),
-                  lastSlash = uri.path.lastIndexOf('/'),
-                  path = uri.path.substr(1, lastSlash);
-
-                self._dsn = dsn;
-                self._globalKey = uri.user;
-                self._globalSecret = uri.pass && uri.pass.substr(1);
-                self._globalProject = uri.path.substr(lastSlash + 1);
-
-                self._globalServer = self._getGlobalServer(uri);
-
-                self._globalEndpoint =
-                  self._globalServer +
-                  '/' +
-                  path +
-                  'api/' +
-                  self._globalProject +
-                  '/store/';
-
-                // Reset backoff state since we may be pointing at a
-                // new project/server
-                this._resetBackoff();
-              },
-
-              /*
+  setDSN: function(dsn) {
+    var self = this,
+      uri = self._parseDSN(dsn),
+      lastSlash = uri.path.lastIndexOf('/'),
+      path = uri.path.substr(1, lastSlash);
+
+    self._dsn = dsn;
+    self._globalKey = uri.user;
+    self._globalSecret = uri.pass && uri.pass.substr(1);
+    self._globalProject = uri.path.substr(lastSlash + 1);
+
+    self._globalServer = self._getGlobalServer(uri);
+
+    self._globalEndpoint =
+      self._globalServer + '/' + path + 'api/' + self._globalProject + '/store/';
+
+    // Reset backoff state since we may be pointing at a
+    // new project/server
+    this._resetBackoff();
+  },
+
+  /*
      * Wrap code within a context so Raven can capture errors
      * reliably across domains that is executed immediately.
      *
      * @param {object} options A specific set of options for this context [optional]
      * @param {function} func The callback to be immediately executed within the context
      * @param {array} args An array of arguments to be called with the callback [optional]
      */
-              context: function(options, func, args) {
-                if (isFunction(options)) {
-                  args = func || [];
-                  func = options;
-                  options = undefined;
-                }
-
-                return this.wrap(options, func).apply(this, args);
-              },
-
-              /*
+  context: function(options, func, args) {
+    if (isFunction(options)) {
+      args = func || [];
+      func = options;
+      options = undefined;
+    }
+
+    return this.wrap(options, func).apply(this, args);
+  },
+
+  /*
      * Wrap code within a context and returns back a new function to be executed
      *
      * @param {object} options A specific set of options for this context [optional]
      * @param {function} func The function to be wrapped in a new context
      * @param {function} func A function to call before the try/catch wrapper [optional, private]
      * @return {function} The newly wrapped functions with a context
      */
-              wrap: function(options, func, _before) {
-                var self = this;
-                // 1 argument has been passed, and it's not a function
-                // so just return it
-                if (isUndefined(func) && !isFunction(options)) {
-                  return options;
-                }
-
-                // options is optional
-                if (isFunction(options)) {
-                  func = options;
-                  options = undefined;
-                }
-
-                // At this point, we've passed along 2 arguments, and the second one
-                // is not a function either, so we'll just return the second argument.
-                if (!isFunction(func)) {
-                  return func;
-                }
-
-                // We don't wanna wrap it twice!
-                try {
-                  if (func.__raven__) {
-                    return func;
-                  }
-
-                  // If this has already been wrapped in the past, return that
-                  if (func.__raven_wrapper__) {
-                    return func.__raven_wrapper__;
-                  }
-                } catch (e) {
-                  // Just accessing custom props in some Selenium environments
-                  // can cause a "Permission denied" exception (see raven-js#495).
-                  // Bail on wrapping and return the function as-is (defers to window.onerror).
-                  return func;
-                }
-
-                function wrapped() {
-                  var args = [],
-                    i = arguments.length,
-                    deep = !options || (options && options.deep !== false);
-
-                  if (_before && isFunction(_before)) {
-                    _before.apply(this, arguments);
-                  }
-
-                  // Recursively wrap all of a function's arguments that are
-                  // functions themselves.
-                  while (i--)
-                    args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
-
-                  try {
-                    // Attempt to invoke user-land function
-                    // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
-                    //       means Raven caught an error invoking your application code. This is
-                    //       expected behavior and NOT indicative of a bug with Raven.js.
-                    return func.apply(this, args);
-                  } catch (e) {
-                    self._ignoreNextOnError();
-                    self.captureException(e, options);
-                    throw e;
-                  }
-                }
-
-                // copy over properties of the old function
-                for (var property in func) {
-                  if (hasKey(func, property)) {
-                    wrapped[property] = func[property];
-                  }
-                }
-                wrapped.prototype = func.prototype;
-
-                func.__raven_wrapper__ = wrapped;
-                // Signal that this function has been wrapped already
-                // for both debugging and to prevent it to being wrapped twice
-                wrapped.__raven__ = true;
-                wrapped.__inner__ = func;
-
-                return wrapped;
-              },
-
-              /*
-     * Uninstalls the global error handler.
-     *
-     * @return {Raven}
-     */
-              uninstall: function() {
-                TraceKit.report.uninstall();
-
-                this._restoreBuiltIns();
-
-                Error.stackTraceLimit = this._originalErrorStackTraceLimit;
-                this._isRavenInstalled = false;
-
-                return this;
-              },
-
-              /*
-     * Manually capture an exception and send it over to Sentry
-     *
-     * @param {error} ex An exception to be logged
-     * @param {object} options A specific set of options for this error [optional]
-     * @return {Raven}
-     */
-              captureException: function(ex, options) {
-                // If not an Error is passed through, recall as a message instead
-                if (!isError(ex)) {
-                  return this.captureMessage(
-                    ex,
-                    objectMerge(
-                      {
-                        trimHeadFrames: 1,
-                        stacktrace: true // if we fall back to captureMessage, default to attempting a new trace
-                      },
-                      options
-                    )
-                  );
-                }
-
-                // Store the raw exception object for potential debugging and introspection
-                this._lastCapturedException = ex;
-
-                // TraceKit.report will re-raise any exception passed to it,
-                // which means you have to wrap it in try/catch. Instead, we
-                // can wrap it here and only re-raise if TraceKit.report
-                // raises an exception different from the one we asked to
-                // report on.
-                try {
-                  var stack = TraceKit.computeStackTrace(ex);
-                  this._handleStackInfo(stack, options);
-                } catch (ex1) {
-                  if (ex !== ex1) {
-                    throw ex1;
-                  }
-                }
-
-                return this;
-              },
-
-              /*
+  wrap: function(options, func, _before) {
+    var self = this;
+    // 1 argument has been passed, and it's not a function
+    // so just return it
+    if (isUndefined(func) && !isFunction(options)) {
+      return options;
+    }
+
+    // options is optional
+    if (isFunction(options)) {
+      func = options;
+      options = undefined;
+    }
+
+    // At this point, we've passed along 2 arguments, and the second one
+    // is not a function either, so we'll just return the second argument.
+    if (!isFunction(func)) {
+      return func;
+    }
+
+    // We don't wanna wrap it twice!
+    try {
+      if (func.__raven__) {
+        return func;
+      }
+
+      // If this has already been wrapped in the past, return that
+      if (func.__raven_wrapper__) {
+        return func.__raven_wrapper__;
+      }
+    } catch (e) {
+      // Just accessing custom props in some Selenium environments
+      // can cause a "Permission denied" exception (see raven-js#495).
+      // Bail on wrapping and return the function as-is (defers to window.onerror).
+      return func;
+    }
+
+    function wrapped() {
+      var args = [],
+        i = arguments.length,
+        deep = !options || (options && options.deep !== false);
+
+      if (_before && isFunction(_before)) {
+        _before.apply(this, arguments);
+      }
+
+      // Recursively wrap all of a function's arguments that are
+      // functions themselves.
+      while (i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i];
+
+      try {
+        // Attempt to invoke user-land function
+        // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
+        //       means Raven caught an error invoking your application code. This is
+        //       expected behavior and NOT indicative of a bug with Raven.js.
+        return func.apply(this, args);
+      } catch (e) {
+        self._ignoreNextOnError();
+        self.captureException(e, options);
+        throw e;
+      }
+    }
+
+    // copy over properties of the old function
+    for (var property in func) {
+      if (hasKey(func, property)) {
+        wrapped[property] = func[property];
+      }
+    }
+    wrapped.prototype = func.prototype;
+
+    func.__raven_wrapper__ = wrapped;
+    // Signal that this function has been wrapped/filled already
+    // for both debugging and to prevent it to being wrapped/filled twice
+    wrapped.__raven__ = true;
+    wrapped.__orig__ = func;
+
+    return wrapped;
+  },
+
+  /**
+   * Uninstalls the global error handler.
+   *
+   * @return {Raven}
+   */
+  uninstall: function() {
+    TraceKit.report.uninstall();
+
+    this._detachPromiseRejectionHandler();
+    this._unpatchFunctionToString();
+    this._restoreBuiltIns();
+    this._restoreConsole();
+
+    Error.stackTraceLimit = this._originalErrorStackTraceLimit;
+    this._isRavenInstalled = false;
+
+    return this;
+  },
+
+  /**
+   * Callback used for `unhandledrejection` event
+   *
+   * @param {PromiseRejectionEvent} event An object containing
+   *   promise: the Promise that was rejected
+   *   reason: the value with which the Promise was rejected
+   * @return void
+   */
+  _promiseRejectionHandler: function(event) {
+    this._logDebug('debug', 'Raven caught unhandled promise rejection:', event);
+    this.captureException(event.reason, {
+      extra: {
+        unhandledPromiseRejection: true
+      }
+    });
+  },
+
+  /**
+   * Installs the global promise rejection handler.
+   *
+   * @return {raven}
+   */
+  _attachPromiseRejectionHandler: function() {
+    this._promiseRejectionHandler = this._promiseRejectionHandler.bind(this);
+    _window.addEventListener &&
+      _window.addEventListener('unhandledrejection', this._promiseRejectionHandler);
+    return this;
+  },
+
+  /**
+   * Uninstalls the global promise rejection handler.
+   *
+   * @return {raven}
+   */
+  _detachPromiseRejectionHandler: function() {
+    _window.removeEventListener &&
+      _window.removeEventListener('unhandledrejection', this._promiseRejectionHandler);
+    return this;
+  },
+
+  /**
+   * Manually capture an exception and send it over to Sentry
+   *
+   * @param {error} ex An exception to be logged
+   * @param {object} options A specific set of options for this error [optional]
+   * @return {Raven}
+   */
+  captureException: function(ex, options) {
+    options = objectMerge({trimHeadFrames: 0}, options ? options : {});
+
+    if (isErrorEvent(ex) && ex.error) {
+      // If it is an ErrorEvent with `error` property, extract it to get actual Error
+      ex = ex.error;
+    } else if (isError(ex)) {
+      // we have a real Error object
+      ex = ex;
+    } else if (isPlainObject(ex)) {
+      // If it is plain Object, serialize it manually and extract options
+      // This will allow us to group events based on top-level keys
+      // which is much better than creating new group when any key/value change
+      options = this._getCaptureExceptionOptionsFromPlainObject(options, ex);
+      ex = new Error(options.message);
+    } else {
+      // If none of previous checks were valid, then it means that
+      // it's not a plain Object
+      // it's not a valid ErrorEvent (one with an error property)
+      // it's not an Error
+      // So bail out and capture it as a simple message:
+      return this.captureMessage(
+        ex,
+        objectMerge(options, {
+          stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
+          trimHeadFrames: options.trimHeadFrames + 1
+        })
+      );
+    }
+
+    // Store the raw exception object for potential debugging and introspection
+    this._lastCapturedException = ex;
+
+    // TraceKit.report will re-raise any exception passed to it,
+    // which means you have to wrap it in try/catch. Instead, we
+    // can wrap it here and only re-raise if TraceKit.report
+    // raises an exception different from the one we asked to
+    // report on.
+    try {
+      var stack = TraceKit.computeStackTrace(ex);
+      this._handleStackInfo(stack, options);
+    } catch (ex1) {
+      if (ex !== ex1) {
+        throw ex1;
+      }
+    }
+
+    return this;
+  },
+
+  _getCaptureExceptionOptionsFromPlainObject: function(currentOptions, ex) {
+    var exKeys = Object.keys(ex).sort();
+    var options = objectMerge(currentOptions, {
+      message:
+        'Non-Error exception captured with keys: ' + serializeKeysForMessage(exKeys),
+      fingerprint: [md5(exKeys)],
+      extra: currentOptions.extra || {}
+    });
+    options.extra.__serialized__ = serializeException(ex);
+
+    return options;
+  },
+
+  /*
      * Manually send a message to Sentry
      *
      * @param {string} msg A plain message to be captured in Sentry
      * @param {object} options A specific set of options for this message [optional]
      * @return {Raven}
      */
-              captureMessage: function(msg, options) {
-                // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
-                // early call; we'll error on the side of logging anything called before configuration since it's
-                // probably something you should see:
-                if (
-                  !!this._globalOptions.ignoreErrors.test &&
-                  this._globalOptions.ignoreErrors.test(msg)
-                ) {
-                  return;
-                }
-
-                options = options || {};
-
-                var data = objectMerge(
-                  {
-                    message: msg + '' // Make sure it's actually a string
-                  },
-                  options
-                );
-
-                if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
-                  var ex;
-                  // Generate a "synthetic" stack trace from this point.
-                  // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
-                  //       of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
-                  //       or if it catches a thrown object without a "stack" property.
-                  try {
-                    throw new Error(msg);
-                  } catch (ex1) {
-                    ex = ex1;
-                  }
-
-                  // null exception name so `Error` isn't prefixed to msg
-                  ex.name = null;
-
-                  options = objectMerge(
-                    {
-                      // fingerprint on msg, not stack trace (legacy behavior, could be
-                      // revisited)
-                      fingerprint: msg,
-                      // since we know this is a synthetic trace, the top N-most frames
-                      // MUST be from Raven.js, so mark them as in_app later by setting
-                      // trimHeadFrames
-                      trimHeadFrames: (options.trimHeadFrames || 0) + 1
-                    },
-                    options
-                  );
-
-                  var stack = TraceKit.computeStackTrace(ex);
-                  var frames = this._prepareFrames(stack, options);
-                  data.stacktrace = {
-                    // Sentry expects frames oldest to newest
-                    frames: frames.reverse()
-                  };
-                }
-
-                // Fire away!
-                this._send(data);
-
-                return this;
-              },
-
-              captureBreadcrumb: function(obj) {
-                var crumb = objectMerge(
-                  {
-                    timestamp: now() / 1000
-                  },
-                  obj
-                );
-
-                if (isFunction(this._globalOptions.breadcrumbCallback)) {
-                  var result = this._globalOptions.breadcrumbCallback(crumb);
-
-                  if (isObject(result) && !isEmptyObject(result)) {
-                    crumb = result;
-                  } else if (result === false) {
-                    return this;
-                  }
-                }
-
-                this._breadcrumbs.push(crumb);
-                if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
-                  this._breadcrumbs.shift();
-                }
-                return this;
-              },
-
-              addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
-                var pluginArgs = [].slice.call(arguments, 1);
-
-                this._plugins.push([plugin, pluginArgs]);
-                if (this._isRavenInstalled) {
-                  this._drainPlugins();
-                }
-
-                return this;
-              },
-
-              /*
+  captureMessage: function(msg, options) {
+    // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
+    // early call; we'll error on the side of logging anything called before configuration since it's
+    // probably something you should see:
+    if (
+      !!this._globalOptions.ignoreErrors.test &&
+      this._globalOptions.ignoreErrors.test(msg)
+    ) {
+      return;
+    }
+
+    options = options || {};
+    msg = msg + ''; // Make sure it's actually a string
+
+    var data = objectMerge(
+      {
+        message: msg
+      },
+      options
+    );
+
+    var ex;
+    // Generate a "synthetic" stack trace from this point.
+    // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative
+    //       of a bug with Raven.js. Sentry generates synthetic traces either by configuration,
+    //       or if it catches a thrown object without a "stack" property.
+    try {
+      throw new Error(msg);
+    } catch (ex1) {
+      ex = ex1;
+    }
+
+    // null exception name so `Error` isn't prefixed to msg
+    ex.name = null;
+    var stack = TraceKit.computeStackTrace(ex);
+
+    // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
+    var initialCall = isArray(stack.stack) && stack.stack[1];
+    var fileurl = (initialCall && initialCall.url) || '';
+
+    if (
+      !!this._globalOptions.ignoreUrls.test &&
+      this._globalOptions.ignoreUrls.test(fileurl)
+    ) {
+      return;
+    }
+
+    if (
+      !!this._globalOptions.whitelistUrls.test &&
+      !this._globalOptions.whitelistUrls.test(fileurl)
+    ) {
+      return;
+    }
+
+    if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
+      // fingerprint on msg, not stack trace (legacy behavior, could be revisited)
+      data.fingerprint = data.fingerprint == null ? msg : data.fingerprint;
+
+      options = objectMerge(
+        {
+          trimHeadFrames: 0
+        },
+        options
+      );
+      // Since we know this is a synthetic trace, the top frame (this function call)
+      // MUST be from Raven.js, so mark it for trimming
+      // We add to the trim counter so that callers can choose to trim extra frames, such
+      // as utility functions.
+      options.trimHeadFrames += 1;
+
+      var frames = this._prepareFrames(stack, options);
+      data.stacktrace = {
+        // Sentry expects frames oldest to newest
+        frames: frames.reverse()
+      };
+    }
+
+    // Make sure that fingerprint is always wrapped in an array
+    if (data.fingerprint) {
+      data.fingerprint = isArray(data.fingerprint)
+        ? data.fingerprint
+        : [data.fingerprint];
+    }
+
+    // Fire away!
+    this._send(data);
+
+    return this;
+  },
+
+  captureBreadcrumb: function(obj) {
+    var crumb = objectMerge(
+      {
+        timestamp: now() / 1000
+      },
+      obj
+    );
+
+    if (isFunction(this._globalOptions.breadcrumbCallback)) {
+      var result = this._globalOptions.breadcrumbCallback(crumb);
+
+      if (isObject(result) && !isEmptyObject(result)) {
+        crumb = result;
+      } else if (result === false) {
+        return this;
+      }
+    }
+
+    this._breadcrumbs.push(crumb);
+    if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
+      this._breadcrumbs.shift();
+    }
+    return this;
+  },
+
+  addPlugin: function(plugin /*arg1, arg2, ... argN*/) {
+    var pluginArgs = [].slice.call(arguments, 1);
+
+    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;
-
-                return this;
-              },
-
-              /*
+  setUserContext: function(user) {
+    // Intentionally do not merge here since that's an unexpected behavior.
+    this._globalContext.user = user;
+
+    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);
-
-                return this;
-              },
-
-              /*
+  setExtraContext: function(extra) {
+    this._mergeContext('extra', extra);
+
+    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);
-
-                return this;
-              },
-
-              /*
+  setTagsContext: function(tags) {
+    this._mergeContext('tags', tags);
+
+    return this;
+  },
+
+  /*
      * Clear all of the context.
      *
      * @return {Raven}
      */
-              clearContext: function() {
-                this._globalContext = {};
-
-                return this;
-              },
-
-              /*
+  clearContext: function() {
+    this._globalContext = {};
+
+    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;
-
-                return this;
-              },
-
-              /*
+  setEnvironment: function(environment) {
+    this._globalOptions.environment = environment;
+
+    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;
-
-                return this;
-              },
-
-              /*
+  setRelease: function(release) {
+    this._globalOptions.release = release;
+
+    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;
-              },
-
-              /**
-     * Override the default HTTP transport mechanism that transmits data
-     * to the Sentry server.
-     *
-     * @param {function} transport Function invoked instead of the default
-     *                             `makeRequest` handler.
-     *
-     * @return {Raven}
-     */
-              setTransport: function(transport) {
-                this._globalOptions.transport = transport;
-
-                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.
+   *
+   * @param {function} transport Function invoked instead of the default
+   *                             `makeRequest` handler.
+   *
+   * @return {Raven}
+   */
+  setTransport: function(transport) {
+    this._globalOptions.transport = transport;
+
+    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;
-              },
-
-              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();
-                }
-              },
-
-              showReportDialog: function(options) {
-                if (
-                  !_document // doesn't work without a document (React native)
-                )
-                  return;
-
-                options = options || {};
-
-                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 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);
+  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?
+
+    // 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;
+
+    options = options || {};
+
+    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 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 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;
+    });
+  },
+
+  _triggerEvent: function(eventType, options) {
+    // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
+    var evt, key;
+
+    if (!this._hasDocument) return;
+
+    options = options || {};
+
+    eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1);
+
+    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 = '<unknown>';
+      }
+
+      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);
+    };
+  },
+
+  /**
+   * 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
+      }
+    });
+  },
+
+  _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]);
+        }
+      };
+    }
+
+    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);
                 }
-
-                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;
-                });
-              },
-
-              _triggerEvent: function(eventType, options) {
-                // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
-                var evt, key;
-
-                if (!this._hasDocument) return;
-
-                options = options || {};
-
-                eventType =
-                  'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1);
-
-                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
+              } 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 {
-                    _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 = '<unknown>';
-                  }
-
-                  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;
+                    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;
                   }
-                  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);
+                  if (eventType === 'click') return clickHandler(evt);
+                  else if (eventType === 'keypress') return keypressHandler(evt);
                 };
-              },
-
-              /**
-     * 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
-                  }
-                });
-              },
-
-              /**
-     * 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]);
-                    }
-                  };
-                }
-
-                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
-                    );
-                  }
-                }
-
-                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]);
-                }
-              },
-
-              /**
-     * Instrument browser built-ins w/ breadcrumb capturing
-     *  - XMLHttpRequests
-     *  - DOM interactions (click/typing)
-     *  - window.location changes
-     *  - console
-     *
-     * Can be disabled or individually configured via the `autoBreadcrumbs` config option
-     */
-              _instrumentBreadcrumbs: function() {
-                var self = this;
-                var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
-
-                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
-                  }
+              }
+              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
+      );
+    }
+
+    // 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)
+   *  - window.location changes
+   *  - console
+   *
+   * Can be disabled or individually configured via the `autoBreadcrumbs` config option
+   */
+  _instrumentBreadcrumbs: function() {
+    var self = this;
+    var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs;
+
+    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
+      }
+    }
+
+    if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) {
+      var xhrproto = _window.XMLHttpRequest && _window.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() {
+            // 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 */
                 }
 
-                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 && '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
-                  );
-                }
-
-                // 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());
-                  }
-                }
-
-                // 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
-                  );
-                }
-
-                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);
-                  });
-                }
-              },
-
-              _restoreBuiltIns: function() {
-                // restore any wrapped builtins
-                var builtin;
-                while (this._wrappedBuiltIns.length) {
-                  builtin = this._wrappedBuiltIns.shift();
-
-                  var obj = builtin[0],
-                    name = builtin[1],
-                    orig = builtin[2];
-
-                  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);
-                }
-
-                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 dsn;
-              },
-
-              _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
+                self.captureBreadcrumb({
+                  type: 'http',
+                  category: 'xhr',
+                  data: xhr.__raven_xhr
                 });
-
-                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 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 (
-                  !!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);
-                }
-
-                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;
-                }
-              },
-
-              _getHttpData: function() {
-                if (!this._hasNavigator && !this._hasDocument) return;
-                var httpData = {};
-
-                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;
-                  }
-                }
-
-                return httpData;
-              },
-
-              _resetBackoff: function() {
-                this._backoffDuration = 0;
-                this._backoffStart = null;
-              },
-
-              _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
-     *
-     * NOTE: This has to be done at this level because TraceKit can generate
-     *       data from window.onerror WITHOUT an exception object (IE8, IE9,
-     *       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);
-                }
-
-                return true;
-              },
-
-              _setBackoffState: function(request) {
-                // If we are already in a backoff state, don't change anything
-                if (this._shouldBackoff()) {
-                  return;
-                }
-
-                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 */
-                }
-
-                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;
-
-                this._backoffStart = now();
-              },
-
-              _send: function(data) {
-                var globalOptions = this._globalOptions;
-
-                var baseData = {
-                    project: this._globalProject,
-                    logger: globalOptions.logger,
-                    platform: 'javascript'
-                  },
-                  httpData = this._getHttpData();
-
-                if (httpData) {
-                  baseData.request = httpData;
-                }
-
-                // 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)
-                  };
-                }
-
-                // If there are no tags/extra, strip the key from the payload alltogther.
-                if (isEmptyObject(data.tags)) delete data.tags;
-
-                if (this._globalContext.user) {
-                  // sentry.interfaces.User
-                  data.user = this._globalContext.user;
-                }
-
-                // Include the environment if it's defined in globalOptions
-                if (globalOptions.environment)
-                  data.environment = globalOptions.environment;
-
-                // Include the release if it's defined in globalOptions
-                if (globalOptions.release) data.release = globalOptions.release;
-
-                // Include server_name if it's defined in globalOptions
-                if (globalOptions.serverName) data.server_name = globalOptions.serverName;
-
-                if (isFunction(globalOptions.dataCallback)) {
-                  data = globalOptions.dataCallback(data) || data;
-                }
-
-                // Why??????????
-                if (!data || isEmptyObject(data)) {
-                  return;
-                }
-
-                // 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;
-                }
-
-                if (typeof globalOptions.sampleRate === 'number') {
-                  if (Math.random() < globalOptions.sampleRate) {
-                    this._sendProcessedPayload(data);
-                  }
-                } else {
-                  this._sendProcessedPayload(data);
-                }
-              },
-
-              _getUuid: function() {
-                return uuid4();
-              },
-
-              _sendProcessedPayload: function(data, callback) {
-                var self = this;
-                var globalOptions = this._globalOptions;
-
-                if (!this.isSetup()) return;
-
-                // Try and clean up the packet before sending by truncating long values
-                data = this._trimPacket(data);
-
-                // 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;
-                }
-
-                // 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());
-
-                // Store outbound payload after trim
-                this._lastData = data;
-
-                this._logDebug('debug', 'Raven about to send:', data);
-
-                var auth = {
-                  sentry_version: '7',
-                  sentry_client: 'raven-js/' + this.VERSION,
-                  sentry_key: this._globalKey
-                };
-
-                if (this._globalSecret) {
-                  auth.sentry_secret = this._globalSecret;
-                }
-
-                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
-                });
-
-                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
-                  );
-                }
-              }
-            };
-
-            /*------------------------------------------------
- * utils
- *
- * conditionally exported for test via Raven.utils
- =================================================
- */
-            var objectPrototype = Object.prototype;
-
-            function isUndefined(what) {
-              return what === void 0;
-            }
-
-            function isFunction(what) {
-              return typeof what === 'function';
-            }
-
-            function isString(what) {
-              return objectPrototype.toString.call(what) === '[object String]';
-            }
-
-            function isEmptyObject(what) {
-              for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars
-              return true;
-            }
-
-            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;
+            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 && supportsFetch()) {
+      fill(
+        _window,
+        'fetch',
+        function(origFetch) {
+          return function() {
+            // 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 if ('Request' in _window && fetchInput instanceof _window.Request) {
+              url = fetchInput.url;
+              if (fetchInput.method) {
+                method = fetchInput.method;
               }
-              each(obj2, function(key, value) {
-                obj1[key] = value;
+            } else {
+              url = '' + fetchInput;
+            }
+
+            // if Sentry key appears in URL, don't capture, as it's our own request
+            if (url.indexOf(self._globalKey) !== -1) {
+              return origFetch.apply(this, args);
+            }
+
+            if (args[1] && args[1].method) {
+              method = args[1].method;
+            }
+
+            var fetchData = {
+              method: method,
+              url: url,
+              status_code: null
+            };
+
+            return origFetch.apply(this, args).then(function(response) {
+              fetchData.status_code = response.status;
+
+              self.captureBreadcrumb({
+                type: 'http',
+                category: 'fetch',
+                data: fetchData
               });
-              return obj1;
+
+              return response;
+            });
+          };
+        },
+        wrappedBuiltIns
+      );
+    }
+
+    // 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 if(_document.attachEvent){
+        // IE8 Compatibility
+        _document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
+        _document.attachEvent('onkeypress', self._keypressEventHandler());
+      }
+    }
+
+    // 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 + '');
+          }
+
+          return origHistFunction.apply(this, arguments);
+        };
+      };
+
+      fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns);
+      fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns);
+    }
+
+    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);
+      });
+    }
+  },
+
+  _restoreBuiltIns: function() {
+    // restore any wrapped builtins
+    var builtin;
+    while (this._wrappedBuiltIns.length) {
+      builtin = this._wrappedBuiltIns.shift();
+
+      var obj = builtin[0],
+        name = builtin[1],
+        orig = builtin[2];
+
+      obj[name] = orig;
+    }
+  },
+
+  _restoreConsole: function() {
+    // eslint-disable-next-line guard-for-in
+    for (var method in this._originalConsoleMethods) {
+      this._originalConsole[method] = this._originalConsoleMethods[method];
+    }
+  },
+
+  _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);
+    }
+
+    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 dsn;
+  },
+
+  _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 (
+      !!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);
+    }
+
+    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;
+    }
+  },
+
+  _getHttpData: function() {
+    if (!this._hasNavigator && !this._hasDocument) return;
+    var httpData = {};
+
+    if (this._hasNavigator && _navigator.userAgent) {
+      httpData.headers = {
+        'User-Agent': navigator.userAgent
+      };
+    }
+
+    // Check in `window` instead of `document`, as we may be in ServiceWorker environment
+    if (_window.location && _window.location.href) {
+      httpData.url = _window.location.href;
+    }
+
+    if (this._hasDocument && _document.referrer) {
+      if (!httpData.headers) httpData.headers = {};
+      httpData.headers.Referer = _document.referrer;
+    }
+
+    return httpData;
+  },
+
+  _resetBackoff: function() {
+    this._backoffDuration = 0;
+    this._backoffStart = null;
+  },
+
+  _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
+   *
+   * NOTE: This has to be done at this level because TraceKit can generate
+   *       data from window.onerror WITHOUT an exception object (IE8, IE9,
+   *       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);
+    }
+
+    return true;
+  },
+
+  _setBackoffState: function(request) {
+    // If we are already in a backoff state, don't change anything
+    if (this._shouldBackoff()) {
+      return;
+    }
+
+    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
+      if (supportsFetch()) {
+        retry = request.headers.get('Retry-After');
+      } else {
+        retry = request.getResponseHeader('Retry-After');
+      }
+
+      // Retry-After is returned in seconds
+      retry = parseInt(retry, 10) * 1000;
+    } catch (e) {
+      /* eslint no-empty:0 */
+    }
+
+    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;
+
+    this._backoffStart = now();
+  },
+
+  _send: function(data) {
+    var globalOptions = this._globalOptions;
+
+    var baseData = {
+        project: this._globalProject,
+        logger: globalOptions.logger,
+        platform: 'javascript'
+      },
+      httpData = this._getHttpData();
+
+    if (httpData) {
+      baseData.request = httpData;
+    }
+
+    // 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)
+      };
+    }
+
+    if (this._globalContext.user) {
+      // sentry.interfaces.User
+      data.user = this._globalContext.user;
+    }
+
+    // Include the environment if it's defined in globalOptions
+    if (globalOptions.environment) data.environment = globalOptions.environment;
+
+    // Include the release if it's defined in globalOptions
+    if (globalOptions.release) data.release = globalOptions.release;
+
+    // Include server_name if it's defined in globalOptions
+    if (globalOptions.serverName) data.server_name = globalOptions.serverName;
+
+    data = this._sanitizeData(data);
+
+    // Cleanup empty properties before sending them to the server
+    Object.keys(data).forEach(function(key) {
+      if (data[key] == null || data[key] === '' || isEmptyObject(data[key])) {
+        delete data[key];
+      }
+    });
+
+    if (isFunction(globalOptions.dataCallback)) {
+      data = globalOptions.dataCallback(data) || data;
+    }
+
+    // Why??????????
+    if (!data || isEmptyObject(data)) {
+      return;
+    }
+
+    // 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;
+    }
+
+    if (typeof globalOptions.sampleRate === 'number') {
+      if (Math.random() < globalOptions.sampleRate) {
+        this._sendProcessedPayload(data);
+      }
+    } else {
+      this._sendProcessedPayload(data);
+    }
+  },
+
+  _sanitizeData: function(data) {
+    return sanitize(data, this._globalOptions.sanitizeKeys);
+  },
+
+  _getUuid: function() {
+    return uuid4();
+  },
+
+  _sendProcessedPayload: function(data, callback) {
+    var self = this;
+    var globalOptions = this._globalOptions;
+
+    if (!this.isSetup()) return;
+
+    // Try and clean up the packet before sending by truncating long values
+    data = this._trimPacket(data);
+
+    // 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;
+    }
+
+    // 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());
+
+    // Store outbound payload after trim
+    this._lastData = data;
+
+    this._logDebug('debug', 'Raven about to send:', data);
+
+    var auth = {
+      sentry_version: '7',
+      sentry_client: 'raven-js/' + this.VERSION,
+      sentry_key: this._globalKey
+    };
+
+    if (this._globalSecret) {
+      auth.sentry_secret = this._globalSecret;
+    }
+
+    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
+      });
+    }
+
+    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) {
+    // Auth is intentionally sent as part of query string (NOT as custom HTTP header) to avoid preflight CORS requests
+    var url = opts.url + '?' + urlencode(opts.auth);
+
+    var evaluatedHeaders = null;
+    var evaluatedFetchParameters = {};
+
+    if (opts.options.headers) {
+      evaluatedHeaders = this._evaluateHash(opts.options.headers);
+    }
+
+    if (opts.options.fetchParameters) {
+      evaluatedFetchParameters = this._evaluateHash(opts.options.fetchParameters);
+    }
+
+    if (supportsFetch()) {
+      evaluatedFetchParameters.body = stringify(opts.data);
+
+      var defaultFetchOptions = objectMerge({}, this._fetchDefaults);
+      var fetchOptions = objectMerge(defaultFetchOptions, evaluatedFetchParameters);
+
+      if (evaluatedHeaders) {
+        fetchOptions.headers = evaluatedHeaders;
+      }
+
+      return _window
+        .fetch(url, fetchOptions)
+        .then(function(response) {
+          if (response.ok) {
+            opts.onSuccess && opts.onSuccess();
+          } else {
+            var error = new Error('Sentry error code: ' + response.status);
+            // It's called request only to keep compatibility with XHR interface
+            // and not add more redundant checks in setBackoffState method
+            error.request = response;
+            opts.onError && opts.onError(error);
+          }
+        })
+        ['catch'](function() {
+          opts.onError &&
+            opts.onError(new Error('Sentry error code: network unavailable'));
+        });
+    }
+
+    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;
+
+    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);
+        };
+      }
+    }
+
+    request.open('POST', url);
+
+    if (evaluatedHeaders) {
+      each(evaluatedHeaders, function(key, value) {
+        request.setRequestHeader(key, value);
+      });
+    }
+
+    request.send(stringify(opts.data));
+  },
+
+  _evaluateHash: function(hash) {
+    var evaluated = {};
+
+    for (var key in hash) {
+      if (hash.hasOwnProperty(key)) {
+        var value = hash[key];
+        evaluated[key] = typeof value === 'function' ? value() : value;
+      }
+    }
+
+    return evaluated;
+  },
+
+  _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;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"1":1,"2":2,"5":5,"6":6,"7":7,"8":8}],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;
+
+/**
+ * DISCLAIMER:
+ *
+ * Expose `Client` constructor for cases where user want to track multiple "sub-applications" in one larger app.
+ * It's not meant to be used by a wide audience, so pleaaase make sure that you know what you're doing before using it.
+ * Accidentally calling `install` multiple times, may result in an unexpected behavior that's very hard to debug.
+ *
+ * It's called `Client' to be in-line with Raven Node implementation.
+ *
+ * HOWTO:
+ *
+ * import Raven from 'raven-js';
+ *
+ * const someAppReporter = new Raven.Client();
+ * const someOtherAppReporter = new Raven.Client();
+ *
+ * someAppReporter.config('__DSN__', {
+ *   ...config goes here
+ * });
+ *
+ * someOtherAppReporter.config('__OTHER_DSN__', {
+ *   ...config goes here
+ * });
+ *
+ * someAppReporter.captureMessage(...);
+ * someAppReporter.captureException(...);
+ * someAppReporter.captureBreadcrumb(...);
+ *
+ * someOtherAppReporter.captureMessage(...);
+ * someOtherAppReporter.captureException(...);
+ * someOtherAppReporter.captureBreadcrumb(...);
+ *
+ * It should "just work".
+ */
+module.exports.Client = RavenConstructor;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"3":3}],5:[function(_dereq_,module,exports){
+(function (global){
+var stringify = _dereq_(7);
+
+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 isErrorEvent(value) {
+  return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]';
+}
+
+function isUndefined(what) {
+  return what === void 0;
+}
+
+function isFunction(what) {
+  return typeof what === 'function';
+}
+
+function isPlainObject(what) {
+  return Object.prototype.toString.call(what) === '[object Object]';
+}
+
+function isString(what) {
+  return Object.prototype.toString.call(what) === '[object String]';
+}
+
+function isArray(what) {
+  return Object.prototype.toString.call(what) === '[object Array]';
+}
+
+function isEmptyObject(what) {
+  if (!isPlainObject(what)) return false;
+
+  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 supportsFetch() {
+  if (!('fetch' in _window)) return false;
+
+  try {
+    new Headers(); // eslint-disable-line no-new
+    new Request(''); // eslint-disable-line no-new
+    new Response(); // eslint-disable-line no-new
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
+// https://caniuse.com/#feat=referrer-policy
+// It doesn't. And it throw exception instead of ignoring this parameter...
+// REF: https://github.com/getsentry/raven-js/issues/1233
+function supportsReferrerPolicy() {
+  if (!supportsFetch()) return false;
+
+  try {
+    // eslint-disable-next-line no-new
+    new Request('pickleRick', {
+      referrerPolicy: 'origin'
+    });
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+function supportsPromiseRejectionEvent() {
+  return typeof PromiseRejectionEvent === 'function';
+}
+
+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.
  * So it's ok that objectFrozen returns false if Object.isFrozen is not
  * 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 truncate(str, max) {
-              return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
-            }
-
-            /**
+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';
+}
+
+/**
  * 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 <a/> 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 <a/> href parsing trick because React Native and other
+// environments where DOM might not be available
+function parseUrl(url) {
+  if (typeof url !== 'string') return {};
+  var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
+
+  // 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;
-                }
-
-                out.push(nextStr);
-
-                len += nextStr.length;
-                elem = elem.parentNode;
-              }
-
-              return out.reverse().join(separator);
-            }
-
-            /**
+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);
+
+    len += nextStr.length;
+    elem = elem.parentNode;
+  }
+
+  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 '';
-              }
-
-              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('');
-            }
-
-            /**
+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);
+  }
+
+  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 both parameters are undefined
+ */
+function isBothUndefined(a, b) {
+  return isUndefined(a) && isUndefined(b);
+}
+
+/**
  * Returns true if the two input exception interfaces have the same content
  */
-            function isSameException(ex1, ex2) {
-              if (isOnlyOneTruthy(ex1, ex2)) return false;
-
-              ex1 = ex1.values[0];
-              ex2 = ex2.values[0];
-
-              if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;
-
-              return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
-            }
-
-            /**
+function isSameException(ex1, ex2) {
+  if (isOnlyOneTruthy(ex1, ex2)) return false;
+
+  ex1 = ex1.values[0];
+  ex2 = ex2.values[0];
+
+  if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;
+
+  // in case both stacktraces are undefined, we can't decide so default to false
+  if (isBothUndefined(ex1.stacktrace, ex2.stacktrace)) return false;
+
+  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}
+function fill(obj, name, replacement, track) {
+  if (obj == null) return;
+  var orig = obj[name];
+  obj[name] = replacement(orig);
+  obj[name].__raven__ = true;
+  obj[name].__orig__ = orig;
+  if (track) {
+    track.push([obj, name, orig]);
+  }
+}
+
+/**
+ * Join values in array
+ * @param input array of values to be joined together
+ * @param delimiter string to be placed in-between values
+ * @returns {string}
  */
-            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;
-          }
-
-          module.exports = {
-            isObject: isObject,
-            isError: isError,
-            wrappedCallback: wrappedCallback
-          };
-        },
-        {}
-      ],
-      6: [
-        function(_dereq_, module, exports) {
-          (function(global) {
-            var utils = _dereq_(5);
-
-            /*
+function safeJoin(input, delimiter) {
+  if (!isArray(input)) return '';
+
+  var output = [];
+
+  for (var i = 0; i < input.length; i++) {
+    try {
+      output.push(String(input[i]));
+    } catch (e) {
+      output.push('[value cannot be serialized]');
+    }
+  }
+
+  return output.join(delimiter);
+}
+
+// Default Node.js REPL depth
+var MAX_SERIALIZE_EXCEPTION_DEPTH = 3;
+// 50kB, as 100kB is max payload size, so half sounds reasonable
+var MAX_SERIALIZE_EXCEPTION_SIZE = 50 * 1024;
+var MAX_SERIALIZE_KEYS_LENGTH = 40;
+
+function utf8Length(value) {
+  return ~-encodeURI(value).split(/%..|./).length;
+}
+
+function jsonSize(value) {
+  return utf8Length(JSON.stringify(value));
+}
+
+function serializeValue(value) {
+  var maxLength = 40;
+
+  if (typeof value === 'string') {
+    return value.length <= maxLength ? value : value.substr(0, maxLength - 1) + '\u2026';
+  } else if (
+    typeof value === 'number' ||
+    typeof value === 'boolean' ||
+    typeof value === 'undefined'
+  ) {
+    return value;
+  }
+
+  var type = Object.prototype.toString.call(value);
+
+  // Node.js REPL notation
+  if (type === '[object Object]') return '[Object]';
+  if (type === '[object Array]') return '[Array]';
+  if (type === '[object Function]')
+    return value.name ? '[Function: ' + value.name + ']' : '[Function]';
+
+  return value;
+}
+
+function serializeObject(value, depth) {
+  if (depth === 0) return serializeValue(value);
+
+  if (isPlainObject(value)) {
+    return Object.keys(value).reduce(function(acc, key) {
+      acc[key] = serializeObject(value[key], depth - 1);
+      return acc;
+    }, {});
+  } else if (Array.isArray(value)) {
+    return value.map(function(val) {
+      return serializeObject(val, depth - 1);
+    });
+  }
+
+  return serializeValue(value);
+}
+
+function serializeException(ex, depth, maxSize) {
+  if (!isPlainObject(ex)) return ex;
+
+  depth = typeof depth !== 'number' ? MAX_SERIALIZE_EXCEPTION_DEPTH : depth;
+  maxSize = typeof depth !== 'number' ? MAX_SERIALIZE_EXCEPTION_SIZE : maxSize;
+
+  var serialized = serializeObject(ex, depth);
+
+  if (jsonSize(stringify(serialized)) > maxSize) {
+    return serializeException(ex, depth - 1);
+  }
+
+  return serialized;
+}
+
+function serializeKeysForMessage(keys, maxLength) {
+  if (typeof keys === 'number' || typeof keys === 'string') return keys.toString();
+  if (!Array.isArray(keys)) return '';
+
+  keys = keys.filter(function(key) {
+    return typeof key === 'string';
+  });
+  if (keys.length === 0) return '[object has no keys]';
+
+  maxLength = typeof maxLength !== 'number' ? MAX_SERIALIZE_KEYS_LENGTH : maxLength;
+  if (keys[0].length >= maxLength) return keys[0];
+
+  for (var usedKeys = keys.length; usedKeys > 0; usedKeys--) {
+    var serialized = keys.slice(0, usedKeys).join(', ');
+    if (serialized.length > maxLength) continue;
+    if (usedKeys === keys.length) return serialized;
+    return serialized + '\u2026';
+  }
+
+  return '';
+}
+
+function sanitize(input, sanitizeKeys) {
+  if (!isArray(sanitizeKeys) || (isArray(sanitizeKeys) && sanitizeKeys.length === 0))
+    return input;
+
+  var sanitizeRegExp = joinRegExp(sanitizeKeys);
+  var sanitizeMask = '********';
+  var safeInput;
+
+  try {
+    safeInput = JSON.parse(stringify(input));
+  } catch (o_O) {
+    return input;
+  }
+
+  function sanitizeWorker(workerInput) {
+    if (isArray(workerInput)) {
+      return workerInput.map(function(val) {
+        return sanitizeWorker(val);
+      });
+    }
+
+    if (isPlainObject(workerInput)) {
+      return Object.keys(workerInput).reduce(function(acc, k) {
+        if (sanitizeRegExp.test(k)) {
+          acc[k] = sanitizeMask;
+        } else {
+          acc[k] = sanitizeWorker(workerInput[k]);
+        }
+        return acc;
+      }, {});
+    }
+
+    return workerInput;
+  }
+
+  return sanitizeWorker(safeInput);
+}
+
+module.exports = {
+  isObject: isObject,
+  isError: isError,
+  isErrorEvent: isErrorEvent,
+  isUndefined: isUndefined,
+  isFunction: isFunction,
+  isPlainObject: isPlainObject,
+  isString: isString,
+  isArray: isArray,
+  isEmptyObject: isEmptyObject,
+  supportsErrorEvent: supportsErrorEvent,
+  supportsFetch: supportsFetch,
+  supportsReferrerPolicy: supportsReferrerPolicy,
+  supportsPromiseRejectionEvent: supportsPromiseRejectionEvent,
+  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,
+  safeJoin: safeJoin,
+  serializeException: serializeException,
+  serializeKeysForMessage: serializeKeysForMessage,
+  sanitize: sanitize
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"7":7}],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
  largely re-written and is now maintained as part of raven-js.  Tests for
  this are in test/vendor.
 
  MIT license
 */
 
-            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 : {};
-
-            // 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): )?(.*)$/;
-
-            function getLocationHref() {
-              if (typeof document === 'undefined' || document.location == null) return '';
-
-              return document.location.href;
-            }
-
-            /**
+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 : {};
+
+// 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): )?(.*)$/;
+
+function getLocationHref() {
+  if (typeof document === 'undefined' || document.location == null) return '';
+
+  return document.location.href;
+}
+
+/**
  * TraceKit.report: cross-browser processing of unhandled exceptions
  *
  * Syntax:
  *   TraceKit.report.subscribe(function(stackInfo) { ... })
  *   TraceKit.report.unsubscribe(function(stackInfo) { ... })
  *   TraceKit.report(exception)
  *   try { ...code... } catch(ex) { TraceKit.report(ex); }
  *
@@ -2610,217 +2965,218 @@
  * subscribed handlers. Please note that TraceKit.report will rethrow the
  * exception. This is REQUIRED in order to get a useful stack trace in IE.
  * If the exception does not reach the top of the browser, you will only
  * get a stack trace from the point where TraceKit.report was called.
  *
  * Handlers receive a stackInfo object as described in the
  * TraceKit.computeStackTrace docs.
  */
-            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);
-              }
-
-              /**
-     * 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);
-                  }
-                }
-              }
-
-              /**
-     * Remove all crash handlers.
-     */
-              function unsubscribeAll() {
-                uninstallGlobalHandler();
-                handlers = [];
-              }
-
-              /**
-     * Dispatch stack information to all handlers.
-     * @param {Object.<string, *>} 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;
-                    }
-                  }
-                }
-
-                if (exception) {
-                  throw exception;
-                }
-              }
-
-              var _oldOnerrorHandler, _onErrorHandlerInstalled;
-
-              /**
-     * Ensures all global unhandled exceptions are recorded.
-     * Supported by Gecko and IE.
-     * @param {string} message Error message.
-     * @param {string} url URL of script that generated the exception.
-     * @param {(number|string)} lineNo The line number at which the error
-     * occurred.
-     * @param {?(number|string)} colNo The column number at which the error
-     * 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];
-                    }
-                  }
-
-                  location.func = UNKNOWN_FUNCTION;
-
-                  stack = {
-                    name: name,
-                    message: msg,
-                    url: getLocationHref(),
-                    stack: [location]
-                  };
-                  notifyHandlers(stack, true);
-                }
-
-                if (_oldOnerrorHandler) {
-                  return _oldOnerrorHandler.apply(this, arguments);
-                }
-
-                return false;
-              }
-
-              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 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();
-                  }
-                }
-
-                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)
-                }
-              }
-
-              report.subscribe = subscribe;
-              report.unsubscribe = unsubscribe;
-              report.uninstall = unsubscribeAll;
-              return report;
-            })();
-
-            /**
+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);
+  }
+
+  /**
+   * 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);
+      }
+    }
+  }
+
+  /**
+   * Remove all crash handlers.
+   */
+  function unsubscribeAll() {
+    uninstallGlobalHandler();
+    handlers = [];
+  }
+
+  /**
+   * Dispatch stack information to all handlers.
+   * @param {Object.<string, *>} 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;
+        }
+      }
+    }
+
+    if (exception) {
+      throw exception;
+    }
+  }
+
+  var _oldOnerrorHandler, _onErrorHandlerInstalled;
+
+  /**
+   * Ensures all global unhandled exceptions are recorded.
+   * Supported by Gecko and IE.
+   * @param {string} msg Error message.
+   * @param {string} url URL of script that generated the exception.
+   * @param {(number|string)} lineNo The line number at which the error
+   * occurred.
+   * @param {?(number|string)} colNo The column number at which the error
+   * occurred.
+   * @param {?Error} ex The actual Error object.
+   */
+  function traceKitWindowOnError(msg, url, lineNo, colNo, ex) {
+    var stack = null;
+    // If 'ex' is ErrorEvent, get real Error from inside
+    var exception = utils.isErrorEvent(ex) ? ex.error : ex;
+    // If 'msg' is ErrorEvent, get real message from inside
+    var message = utils.isErrorEvent(msg) ? msg.message : msg;
+
+    if (lastExceptionStack) {
+      TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(
+        lastExceptionStack,
+        url,
+        lineNo,
+        message
+      );
+      processLastException();
+    } else if (exception && utils.isError(exception)) {
+      // non-string `exception` 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(exception);
+      notifyHandlers(stack, true);
+    } else {
+      var location = {
+        url: url,
+        line: lineNo,
+        column: colNo
+      };
+
+      var name = undefined;
+      var groups;
+
+      if ({}.toString.call(message) === '[object String]') {
+        var groups = message.match(ERROR_TYPES_RE);
+        if (groups) {
+          name = groups[1];
+          message = groups[2];
+        }
+      }
+
+      location.func = UNKNOWN_FUNCTION;
+
+      stack = {
+        name: name,
+        message: message,
+        url: getLocationHref(),
+        stack: [location]
+      };
+      notifyHandlers(stack, true);
+    }
+
+    if (_oldOnerrorHandler) {
+      return _oldOnerrorHandler.apply(this, arguments);
+    }
+
+    return false;
+  }
+
+  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 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();
+      }
+    }
+
+    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)
+    }
+  }
+
+  report.subscribe = subscribe;
+  report.unsubscribe = unsubscribe;
+  report.uninstall = unsubscribeAll;
+  return report;
+})();
+
+/**
  * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
  *
  * Syntax:
  *   s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
  * Returns:
  *   s.name              - exception name
  *   s.message           - exception message
  *   s.stack[i].url      - JavaScript or HTML file URL
@@ -2861,414 +3217,658 @@
  *
  * This is okay for tracing (because you are likely to be calling
  * computeStackTrace from the function you want to be the topmost element
  * of the stack trace anyway), but not okay for logging unhandled
  * exceptions (because your catch block will likely be far away from the
  * 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'
-
-              /**
-     * Computes stack trace information from the stack property.
-     * Chrome and Gecko use this property.
-     * @param {Error} ex
-     * @return {?Object.<string, *>} 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|<anonymous>|\/).*?)(?::(\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;
-                  }
-
-                  stack.push(element);
-                }
-
-                if (!stack.length) {
-                  return null;
-                }
-
-                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.<string, *>} stackInfo Stack trace information from
-     * one of the compute* methods.
-     * @param {string} url The URL of the script that caused an error.
-     * @param {(number|string)} lineNo The line number of the script that
-     * caused an error.
-     * @param {string=} message The error generated by the browser, which
-     * hopefully contains the name of the object that caused the error.
-     * @return {boolean} Whether or not the stack information was
-     * augmented.
-     */
-              function augmentStackTraceWithInitialElement(
-                stackInfo,
-                url,
-                lineNo,
-                message
-              ) {
-                var initial = {
-                  url: url,
-                  line: lineNo
-                };
-
-                if (initial.url && initial.line) {
-                  stackInfo.incomplete = false;
-
-                  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;
-                      }
-                    }
-                  }
-
-                  stackInfo.stack.unshift(initial);
-                  stackInfo.partial = true;
-                  return true;
-                } else {
-                  stackInfo.incomplete = true;
-                }
-
-                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
-     * Safari and IE. The top frame is restored by
-     * {@link augmentStackTraceWithInitialElement}.
-     * @param {Error} ex
-     * @return {?Object.<string, *>} 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;
-                  }
-
-                  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 (funcs['' + curr]) {
-                    recursion = true;
-                  } else {
-                    funcs['' + curr] = true;
-                  }
-
-                  stack.push(item);
-                }
-
-                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;
-              }
-
-              /**
-     * 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;
-
-                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()
-                };
-              }
-
-              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) {
-          /*
+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.<string, *>} 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|<anonymous>|[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
+    var winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
+    // NOTE: blob urls are now supposed to always have an origin, therefore it's format
+    // which is `blob:http://url/path/with-some-uuid`, is matched by `blob.*?:\/` as well
+    var gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|moz-extension).*?:\/.*?|\[native code\]|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i;
+    // Used to additionally parse URL/line/column from eval frames
+    var geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
+    var chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/;
+    var lines = ex.stack.split('\n');
+    var stack = [];
+    var submatch;
+    var parts;
+    var element;
+    var 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;
+      }
+
+      stack.push(element);
+    }
+
+    if (!stack.length) {
+      return null;
+    }
+
+    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.<string, *>} stackInfo Stack trace information from
+   * one of the compute* methods.
+   * @param {string} url The URL of the script that caused an error.
+   * @param {(number|string)} lineNo The line number of the script that
+   * caused an error.
+   * @param {string=} message The error generated by the browser, which
+   * hopefully contains the name of the object that caused the error.
+   * @return {boolean} Whether or not the stack information was
+   * augmented.
+   */
+  function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {
+    var initial = {
+      url: url,
+      line: lineNo
+    };
+
+    if (initial.url && initial.line) {
+      stackInfo.incomplete = false;
+
+      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;
+          }
+        }
+      }
+
+      stackInfo.stack.unshift(initial);
+      stackInfo.partial = true;
+      return true;
+    } else {
+      stackInfo.incomplete = true;
+    }
+
+    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
+   * Safari and IE. The top frame is restored by
+   * {@link augmentStackTraceWithInitialElement}.
+   * @param {Error} ex
+   * @return {?Object.<string, *>} 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;
+      }
+
+      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 (funcs['' + curr]) {
+        recursion = true;
+      } else {
+        funcs['' + curr] = true;
+      }
+
+      stack.push(item);
+    }
+
+    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;
+  }
+
+  /**
+   * 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;
+
+    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()
+    };
+  }
+
+  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){
+/*
  json-stringify-safe
  Like JSON.stringify, but doesn't throw on circular references.
 
  Originally forked from https://github.com/isaacs/json-stringify-safe
  version 5.0.1 on 3/8/2017 and modified to handle Errors serialization
  and IE8 compatibility. Tests for this are in test/vendor.
 
  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);
-          }
-
-          // 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];
-              }
-            }
-
-            return err;
-          }
-
-          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('.') + ']'
-                );
-              };
-            }
-
-            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);
-              }
-
-              return replacer == null
-                ? value instanceof Error ? stringifyError(value) : value
-                : replacer.call(this, key, value);
-            };
-          }
-        },
-        {}
-      ]
-    },
-    {},
-    [4]
-  )(4);
-});
+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);
+}
+
+// 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];
+    }
+  }
+
+  return err;
+}
+
+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('.') + ']';
+    };
+  }
+
+  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);
+    }
+
+    return replacer == null
+      ? value instanceof Error ? stringifyError(value) : value
+      : replacer.call(this, key, value);
+  };
+}
+
+},{}],8:[function(_dereq_,module,exports){
+/*
+ * JavaScript MD5
+ * https://github.com/blueimp/JavaScript-MD5
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * https://opensource.org/licenses/MIT
+ *
+ * Based on
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+* Add integers, wrapping at 2^32. This uses 16-bit operations internally
+* to work around bugs in some JS interpreters.
+*/
+function safeAdd(x, y) {
+  var lsw = (x & 0xffff) + (y & 0xffff);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xffff);
+}
+
+/*
+* Bitwise rotate a 32-bit number to the left.
+*/
+function bitRotateLeft(num, cnt) {
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+* These functions implement the four basic operations the algorithm uses.
+*/
+function md5cmn(q, a, b, x, s, t) {
+  return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
+}
+function md5ff(a, b, c, d, x, s, t) {
+  return md5cmn((b & c) | (~b & d), a, b, x, s, t);
+}
+function md5gg(a, b, c, d, x, s, t) {
+  return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+}
+function md5hh(a, b, c, d, x, s, t) {
+  return md5cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5ii(a, b, c, d, x, s, t) {
+  return md5cmn(c ^ (b | ~d), a, b, x, s, t);
+}
+
+/*
+* Calculate the MD5 of an array of little-endian words, and a bit length.
+*/
+function binlMD5(x, len) {
+  /* append padding */
+  x[len >> 5] |= 0x80 << (len % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var i;
+  var olda;
+  var oldb;
+  var oldc;
+  var oldd;
+  var a = 1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d = 271733878;
+
+  for (i = 0; i < x.length; i += 16) {
+    olda = a;
+    oldb = b;
+    oldc = c;
+    oldd = d;
+
+    a = md5ff(a, b, c, d, x[i], 7, -680876936);
+    d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+    c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+    b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+    a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+    d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+    c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+    b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+    a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+    d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+    c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
+    b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+    a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+    d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+    c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+    b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+    a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+    d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+    c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+    b = md5gg(b, c, d, a, x[i], 20, -373897302);
+    a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+    d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+    c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+    b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+    a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+    d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+    c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+    b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+    a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+    d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+    c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+    b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+    a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
+    d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+    c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+    b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+    a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+    d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+    c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+    b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+    a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+    d = md5hh(d, a, b, c, x[i], 11, -358537222);
+    c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+    b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+    a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+    d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+    c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+    b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+    a = md5ii(a, b, c, d, x[i], 6, -198630844);
+    d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+    c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+    b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+    a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+    d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+    c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+    b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+    a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+    d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+    c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+    b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+    a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+    d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+    c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+    b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+    a = safeAdd(a, olda);
+    b = safeAdd(b, oldb);
+    c = safeAdd(c, oldc);
+    d = safeAdd(d, oldd);
+  }
+  return [a, b, c, d];
+}
+
+/*
+* Convert an array of little-endian words to a string
+*/
+function binl2rstr(input) {
+  var i;
+  var output = '';
+  var length32 = input.length * 32;
+  for (i = 0; i < length32; i += 8) {
+    output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
+  }
+  return output;
+}
+
+/*
+* Convert a raw string to an array of little-endian words
+* Characters >255 have their high-byte silently ignored.
+*/
+function rstr2binl(input) {
+  var i;
+  var output = [];
+  output[(input.length >> 2) - 1] = undefined;
+  for (i = 0; i < output.length; i += 1) {
+    output[i] = 0;
+  }
+  var length8 = input.length * 8;
+  for (i = 0; i < length8; i += 8) {
+    output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
+  }
+  return output;
+}
+
+/*
+* Calculate the MD5 of a raw string
+*/
+function rstrMD5(s) {
+  return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
+}
+
+/*
+* Calculate the HMAC-MD5, of a key and some data (raw strings)
+*/
+function rstrHMACMD5(key, data) {
+  var i;
+  var bkey = rstr2binl(key);
+  var ipad = [];
+  var opad = [];
+  var hash;
+  ipad[15] = opad[15] = undefined;
+  if (bkey.length > 16) {
+    bkey = binlMD5(bkey, key.length * 8);
+  }
+  for (i = 0; i < 16; i += 1) {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5c5c5c5c;
+  }
+  hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+  return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
+}
+
+/*
+* Convert a raw string to a hex string
+*/
+function rstr2hex(input) {
+  var hexTab = '0123456789abcdef';
+  var output = '';
+  var x;
+  var i;
+  for (i = 0; i < input.length; i += 1) {
+    x = input.charCodeAt(i);
+    output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+  }
+  return output;
+}
+
+/*
+* Encode a string as utf-8
+*/
+function str2rstrUTF8(input) {
+  return unescape(encodeURIComponent(input));
+}
+
+/*
+* Take string arguments and return either raw or hex encoded strings
+*/
+function rawMD5(s) {
+  return rstrMD5(str2rstrUTF8(s));
+}
+function hexMD5(s) {
+  return rstr2hex(rawMD5(s));
+}
+function rawHMACMD5(k, d) {
+  return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
+}
+function hexHMACMD5(k, d) {
+  return rstr2hex(rawHMACMD5(k, d));
+}
+
+function md5(string, key, raw) {
+  if (!key) {
+    if (!raw) {
+      return hexMD5(string);
+    }
+    return rawMD5(string);
+  }
+  if (!raw) {
+    return hexHMACMD5(key, string);
+  }
+  return rawHMACMD5(key, string);
+}
+
+module.exports = md5;
+
+},{}]},{},[4])(4)
+});
\ No newline at end of file