/*! Raven.js 3.18.1 (2dca364) | github.com/getsentry/raven-js */

/*
 * Includes TraceKit
 * https://github.com/getsentry/TraceKit
 *
 * Copyright 2017 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() {
  var define, module, exports;
  return (function e(t, n, r) {
    function s(o, u) {
      if (!n[o]) {
        if (!t[o]) {
          var a = typeof require == 'function' && require;
          if (!u && a) return a(o, !0);
          if (i) return i(o, !0);
          var f = new Error("Cannot find module '" + o + "'");
          throw ((f.code = 'MODULE_NOT_FOUND'), f);
        }
        var l = (n[o] = {exports: {}});
        t[o][0].call(
          l.exports,
          function(e) {
            var n = t[o][1][e];
            return s(n ? n : e);
          },
          l,
          l.exports,
          e,
          t,
          n,
          r
        );
      }
      return n[o].exports;
    }
    var i = typeof require == 'function' && require;
    for (var o = 0; o < r.length; o++) s(r[o]);
    return s;
  })(
    {
      1: [
        function(_dereq_, module, exports) {
          function RavenConfigError(message) {
            this.name = 'RavenConfigError';
            this.message = message;
          }
          RavenConfigError.prototype = new Error();
          RavenConfigError.prototype.constructor = RavenConfigError;

          module.exports = RavenConfigError;
        },
        {}
      ],
      2: [
        function(_dereq_, module, exports) {
          var wrapMethod = function(console, level, callback) {
            var originalConsoleLevel = console[level];
            var originalConsole = console;

            if (!(level in console)) {
              return;
            }

            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];
              }
            }

            /*
 * 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

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * 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();
              },

              /*
     * 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);
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * Clear all of the context.
     *
     * @return {Raven}
     */
              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));
              },

              /*
     * Set environment of application
     *
     * @param {string} environment Typically something like 'production'.
     * @return {Raven}
     */
              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;
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * 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;
              },

              /*
     * Get the latest raw exception that was captured by Raven.
     *
     * @return {error}
     */
              lastException: function() {
                return this._lastCapturedException;
              },

              /*
     * Get the last event id
     *
     * @return {string}
     */
              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);
                }

                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
                  }
                });
              },

              /**
     * 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
                  }
                }

                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
                });

                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;
              }
              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';
            }

            /**
 * 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);
                });
              }
            }

            /**
 * 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);
            }

            /**
 * 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('');
            }

            /**
 * Returns true if either a OR b is truthy, but not both
 */
            function isOnlyOneTruthy(a, b) {
              return !!(!!a ^ !!b);
            }

            /**
 * Returns true if the two input exception interfaces have the same content
 */
            function isSameException(ex1, ex2) {
              if (isOnlyOneTruthy(ex1, ex2)) return false;

              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);
            }

            /**
 * 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;
            }

            /**
 * Polyfill a method
 * @param obj object e.g. `document`
 * @param name method name present on object e.g. `addEventListener`
 * @param replacement replacement function
 * @param track {optional} record instrumentation to an array
 */
            function fill(obj, name, replacement, track) {
              var orig = obj[name];
              obj[name] = replacement(orig);
              if (track) {
                track.push([obj, name, orig]);
              }
            }

            if (typeof __DEV__ !== 'undefined' && __DEV__) {
              Raven.utils = {
                isUndefined: isUndefined,
                isFunction: isFunction,
                isString: isString,
                isObject: isObject,
                isEmptyObject: isEmptyObject,
                isError: isError,
                each: each,
                objectMerge: objectMerge,
                truncate: truncate,
                hasKey: hasKey,
                joinRegExp: joinRegExp,
                urlencode: urlencode,
                uuid4: uuid4,
                htmlTreeAsString: htmlTreeAsString,
                htmlElementAsString: htmlElementAsString,
                parseUrl: parseUrl,
                fill: fill
              };
            }

            // Deprecations
            Raven.prototype.setUser = Raven.prototype.setUserContext;
            Raven.prototype.setReleaseContext = Raven.prototype.setRelease;

            module.exports = Raven;
          }.call(
            this,
            typeof global !== 'undefined'
              ? global
              : typeof self !== 'undefined'
                ? self
                : typeof window !== 'undefined' ? window : {}
          ));
        },
        {'1': 1, '2': 2, '5': 5, '6': 6, '7': 7}
      ],
      4: [
        function(_dereq_, module, exports) {
          (function(global) {
            /**
 * Enforces a single instance of the Raven client, and the
 * main entry point for Raven. If you are a consumer of the
 * Raven library, you SHOULD load this file (vs raven.js).
 **/

            var RavenConstructor = _dereq_(3);

            // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
            var _window =
              typeof window !== 'undefined'
                ? window
                : typeof global !== 'undefined'
                  ? global
                  : typeof self !== 'undefined' ? self : {};
            var _Raven = _window.Raven;

            var Raven = new RavenConstructor();

            /*
 * Allow multiple versions of Raven to be installed.
 * Strip Raven from the global context and returns the instance.
 *
 * @return {Raven}
 */
            Raven.noConflict = function() {
              _window.Raven = _Raven;
              return Raven;
            };

            Raven.afterLoad();

            module.exports = Raven;
          }.call(
            this,
            typeof global !== 'undefined'
              ? global
              : typeof self !== 'undefined'
                ? self
                : typeof window !== 'undefined' ? window : {}
          ));
        },
        {'3': 3}
      ],
      5: [
        function(_dereq_, module, exports) {
          function isObject(what) {
            return typeof what === 'object' && what !== null;
          }

          // Yanked from https://git.io/vS8DV re-used under CC0
          // with some tiny modifications
          function isError(value) {
            switch ({}.toString.call(value)) {
              case '[object Error]':
                return true;
              case '[object Exception]':
                return true;
              case '[object DOMException]':
                return true;
              default:
                return value instanceof Error;
            }
          }

          function wrappedCallback(callback) {
            function dataCallback(data, original) {
              var normalizedData = callback(data) || data;
              if (original) {
                return original(normalizedData) || normalizedData;
              }
              return normalizedData;
            }

            return dataCallback;
          }

          module.exports = {
            isObject: isObject,
            isError: isError,
            wrappedCallback: wrappedCallback
          };
        },
        {}
      ],
      6: [
        function(_dereq_, module, exports) {
          (function(global) {
            var utils = _dereq_(5);

            /*
 TraceKit - Cross brower stack traces

 This was originally forked from github.com/occ/TraceKit, but has since been
 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;
            }

            /**
 * 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); }
 *
 * Supports:
 *   - Firefox: full stack trace with line numbers, plus column number
 *              on top frame; column number is not guaranteed
 *   - Opera:   full stack trace with line and column numbers
 *   - Chrome:  full stack trace with line and column numbers
 *   - Safari:  line and column number for the top frame only; some frames
 *              may be missing, and column number is not guaranteed
 *   - IE:      line and column number for the top frame only; some frames
 *              may be missing, and column number is not guaranteed
 *
 * In theory, TraceKit should work on all of the following versions:
 *   - IE5.5+ (only 8.0 tested)
 *   - Firefox 0.9+ (only 3.5+ tested)
 *   - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
 *     Exceptions Have Stacktrace to be enabled in opera:config)
 *   - Safari 3+ (only 4+ tested)
 *   - Chrome 1+ (only 5+ tested)
 *   - Konqueror 3.5+ (untested)
 *
 * Requires TraceKit.computeStackTrace.
 *
 * Tries to catch all unhandled exceptions and report them to the
 * 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.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
 *   s.stack[i].func     - function name, or empty for anonymous functions (if guessing did not work)
 *   s.stack[i].args     - arguments passed to the function, if known
 *   s.stack[i].line     - line number, if known
 *   s.stack[i].column   - column number, if known
 *
 * Supports:
 *   - Firefox:  full stack trace with line numbers and unreliable column
 *               number on top frame
 *   - Opera 10: full stack trace with line and column numbers
 *   - Opera 9-: full stack trace with line numbers
 *   - Chrome:   full stack trace with line and column numbers
 *   - Safari:   line and column number for the topmost stacktrace element
 *               only
 *   - IE:       no line numbers whatsoever
 *
 * Tries to guess names of anonymous functions by looking for assignments
 * in the source code. In IE and Safari, we have to guess source file names
 * by searching for function bodies inside all page scripts. This will not
 * work for scripts that are loaded cross-domain.
 * Here be dragons: some function names may be guessed incorrectly, and
 * duplicate functions may be mismatched.
 *
 * TraceKit.computeStackTrace should only be used for tracing purposes.
 * Logging of unhandled exceptions should be done with TraceKit.report,
 * which builds on top of TraceKit.computeStackTrace and provides better
 * IE support by utilizing the window.onerror event to retrieve information
 * about the top of the stack.
 *
 * Note: In IE and Safari, no stack trace is recorded on the Error object,
 * so computeStackTrace instead walks its *own* chain of callers.
 * This means that:
 *  * in Safari, some methods may be missing from the stack trace;
 *  * in IE, the topmost function in the stack trace will always be the
 *    caller of computeStackTrace.
 *
 * 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) {
          /*
 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);
});
