diff --git a/appveyor.yml b/appveyor.yml
index c24b207c..16c64271 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -6,7 +6,6 @@ install:
   - npm install -g gulp
   - npm install -g bower
   - npm install
-  - tsd reinstall -s
   - bower install
 
 build_script:
diff --git a/dist/exceptionless.d.ts b/dist/exceptionless.d.ts
index e37a18f9..cce444f1 100644
--- a/dist/exceptionless.d.ts
+++ b/dist/exceptionless.d.ts
@@ -334,7 +334,9 @@ export declare class ConfigurationDefaultsPlugin implements IEventPlugin {
 export declare class ErrorPlugin implements IEventPlugin {
     priority: number;
     name: string;
+    ignoredProperties: string[];
     run(context: EventPluginContext, next?: () => void): void;
+    private getAdditionalData(exception);
 }
 export declare class ModuleInfoPlugin implements IEventPlugin {
     priority: number;
diff --git a/dist/exceptionless.js b/dist/exceptionless.js
index ea5e06b7..bc085199 100644
--- a/dist/exceptionless.js
+++ b/dist/exceptionless.js
@@ -2271,9 +2271,26 @@ var ErrorPlugin = (function () {
     function ErrorPlugin() {
         this.priority = 30;
         this.name = 'ErrorPlugin';
+        this.ignoredProperties = [
+            'arguments',
+            'column',
+            'columnNumber',
+            'description',
+            'fileName',
+            'message',
+            'name',
+            'number',
+            'line',
+            'lineNumber',
+            'opera#sourceloc',
+            'sourceURL',
+            'stack',
+            'stacktrace'
+        ];
     }
     ErrorPlugin.prototype.run = function (context, next) {
         var ERROR_KEY = '@error';
+        var EXTRA_PROPERTIES_KEY = '@ext';
         var exception = context.contextData.getException();
         if (!!exception) {
             context.event.type = 'error';
@@ -2284,12 +2301,35 @@ var ErrorPlugin = (function () {
                 }
                 var result = parser.parse(context, exception);
                 if (!!result) {
+                    var additionalData = this.getAdditionalData(exception);
+                    if (!!additionalData) {
+                        if (!result.data) {
+                            result.data = {};
+                        }
+                        result.data[EXTRA_PROPERTIES_KEY] = additionalData;
+                    }
                     context.event.data[ERROR_KEY] = result;
                 }
             }
         }
         next && next();
     };
+    ErrorPlugin.prototype.getAdditionalData = function (exception) {
+        var _this = this;
+        var keys = Object.keys(exception)
+            .filter(function (key) { return _this.ignoredProperties.indexOf(key) < 0; });
+        if (keys.length === 0) {
+            return null;
+        }
+        var additionalData = {};
+        keys.forEach(function (key) {
+            var value = exception[key];
+            if (typeof value !== 'function') {
+                additionalData[key] = value;
+            }
+        });
+        return additionalData;
+    };
     return ErrorPlugin;
 })();
 exports.ErrorPlugin = ErrorPlugin;
diff --git a/dist/exceptionless.js.map b/dist/exceptionless.js.map
index 9f90d469..590ed98c 100644
--- a/dist/exceptionless.js.map
+++ b/dist/exceptionless.js.map
@@ -1 +1 @@
-{"version":3,"sources":["tracekit.js","/source/exceptionless.ts"],"names":["getDefaultsSettingsFromScriptTag","processUnhandledException"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7oCA,8BAA8B,+BAA+B,CAAC,CAAA;AAqC9D,mCAAmC,+BAA+B,CAAC,CAAA;AACnE,uCAAuC,mCAAmC,CAAC,CAAA;AAC3E,4CAA4C,wCAAwC,CAAC,CAAA;AAIrF,yCAAyC,uCAAuC,CAAC,CAAA;AAKjF,oCAAoC,uBAAuB,CAAC,CAAA;AAC5D,sBAAsB,SAAS,CAAC,CAAA;AAEhC;IACEA,EAAEA,CAACA,CAACA,CAACA,QAAQA,IAAIA,CAACA,QAAQA,CAACA,oBAAoBA,CAACA,CAACA,CAACA;QAChDA,MAAMA,CAACA,IAAIA,CAACA;IACdA,CAACA;IAEDA,IAAIA,OAAOA,GAAGA,QAAQA,CAACA,oBAAoBA,CAACA,QAAQA,CAACA,CAACA;IACtDA,GAAGA,CAACA,CAACA,GAAGA,CAACA,KAAKA,GAAGA,CAACA,EAAEA,KAAKA,GAAGA,OAAOA,CAACA,MAAMA,EAAEA,KAAKA,EAAEA,EAAEA,CAACA;QACpDA,EAAEA,CAACA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,IAAIA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,OAAOA,CAACA,gBAAgBA,CAACA,GAAGA,CAACA,CAACA,CAACA,CAACA,CAACA;YAC5EA,MAAMA,CAACA,aAAKA,CAACA,gBAAgBA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,KAAKA,CAACA,GAAGA,CAACA,CAACA,GAAGA,EAAEA,CAACA,CAACA;QACrEA,CAACA;IACHA,CAACA;IACDA,MAAMA,CAACA,IAAIA,CAACA;AACdA,CAACA;AAED,mCAAmC,UAA8B,EAAE,OAAY;IAC7EC,IAAIA,OAAOA,GAAGA,yCAAmBA,CAACA,OAAOA,CAACA,wBAAwBA,CAACA,IAAIA,KAAKA,CAACA,UAAUA,CAACA,OAAOA,IAAIA,CAACA,OAAOA,IAAIA,EAAEA,CAACA,CAACA,MAAMA,IAAIA,cAAcA,CAACA,EAAEA,SAASA,CAACA,CAACA;IACzJA,OAAOA,CAACA,iBAAiBA,CAACA,wBAAwBA,CAACA,GAAGA,UAAUA,CAACA;IACjEA,OAAOA,CAACA,MAAMA,EAAEA,CAACA;AACnBA,CAACA;AAmBD,IAAI,QAAQ,GAAG,6BAAa,CAAC,QAAQ,CAAC;AACtC,IAAI,QAAQ,GAAG,gCAAgC,EAAE,CAAC;AAClD,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;AAC1C,CAAC;AAED,QAAQ,CAAC,WAAW,GAAG,IAAI,uCAAkB,EAAE,CAAC;AAChD,QAAQ,CAAC,eAAe,GAAG,IAAI,+CAAsB,EAAE,CAAC;AACxD,QAAQ,CAAC,oBAAoB,GAAG,IAAI,yDAA2B,EAAE,CAAC;AAClE,QAAQ,CAAC,iBAAiB,GAAG,IAAI,mDAAwB,EAAE,CAAC;AAE5D,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;AACrD,QAAQ,CAAC,6BAA6B,EAAE,CAAC;AAUnC,KAAM,CAAC,eAAe,GAAG,QAAQ,CAAC","file":"exceptionless.js","sourcesContent":["/*\n TraceKit - Cross browser stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n    return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n    return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n    return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n    window.TraceKit = _oldTraceKit;\n    return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n    function wrapped() {\n        try {\n            return func.apply(this, arguments);\n        } catch (e) {\n            TraceKit.report(e);\n            throw e;\n        }\n    }\n    return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n *   TraceKit.report.subscribe(function(stackInfo) { ... })\n *   TraceKit.report.unsubscribe(function(stackInfo) { ... })\n *   TraceKit.report(exception)\n *   try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n *   - Firefox: full stack trace with line numbers, plus column number\n *              on top frame; column number is not guaranteed\n *   - Opera:   full stack trace with line and column numbers\n *   - Chrome:  full stack trace with line and column numbers\n *   - Safari:  line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *   - IE:      line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n *   - IE5.5+ (only 8.0 tested)\n *   - Firefox 0.9+ (only 3.5+ tested)\n *   - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n *     Exceptions Have Stacktrace to be enabled in opera:config)\n *   - Safari 3+ (only 4+ tested)\n *   - Chrome 1+ (only 5+ tested)\n *   - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n    var handlers = [],\n        lastException = null,\n        lastExceptionStack = null;\n\n    /**\n     * Add a crash handler.\n     * @param {Function} handler\n     */\n    function subscribe(handler) {\n        installGlobalHandler();\n        handlers.push(handler);\n    }\n\n    /**\n     * Remove a crash handler.\n     * @param {Function} handler\n     */\n    function unsubscribe(handler) {\n        for (var i = handlers.length - 1; i >= 0; --i) {\n            if (handlers[i] === handler) {\n                handlers.splice(i, 1);\n            }\n        }\n    }\n\n    /**\n     * Dispatch stack information to all handlers.\n     * @param {Object.<string, *>} stack\n     */\n    function notifyHandlers(stack, isWindowError) {\n        var exception = null;\n        if (isWindowError && !TraceKit.collectWindowErrors) {\n          return;\n        }\n        for (var i in handlers) {\n            if (_has(handlers, i)) {\n                try {\n                    handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n                } catch (inner) {\n                    exception = inner;\n                }\n            }\n        }\n\n        if (exception) {\n            throw exception;\n        }\n    }\n\n    var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n    /**\n     * Ensures all global unhandled exceptions are recorded.\n     * Supported by Gecko and IE.\n     * @param {string} message Error message.\n     * @param {string} url URL of script that generated the exception.\n     * @param {(number|string)} lineNo The line number at which the error\n     * occurred.\n     * @param {?(number|string)} columnNo The column number at which the error\n     * occurred.\n     * @param {?Error} errorObj The actual Error object.\n     */\n    function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n        var stack = null;\n\n        if (errorObj) {\n          stack = TraceKit.computeStackTrace(errorObj);\n        }\n        else\n        {\n            if (lastExceptionStack) {\n                TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n                stack = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n            } else {\n                var location = {\n                    'url': url,\n                    'line': lineNo,\n                    'column': columnNo\n                };\n                location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n                location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n                stack = {\n                    'mode': 'onerror',\n                    'message': message,\n                    'stack': [location]\n                };\n            }\n        }\n\n        notifyHandlers(stack, 'from window.onerror');\n\n        if (_oldOnerrorHandler) {\n            return _oldOnerrorHandler.apply(this, arguments);\n        }\n\n        return false;\n    }\n\n    function installGlobalHandler ()\n    {\n        if (_onErrorHandlerInstalled === true) {\n            return;\n        }\n        _oldOnerrorHandler = window.onerror;\n        window.onerror = traceKitWindowOnError;\n        _onErrorHandlerInstalled = true;\n    }\n\n    /**\n     * Reports an unhandled Error to TraceKit.\n     * @param {Error} ex\n     */\n    function report(ex) {\n        var args = _slice.call(arguments, 1);\n        if (lastExceptionStack) {\n            if (lastException === ex) {\n                return; // already caught by an inner catch block, ignore\n            } else {\n                var s = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [s, null].concat(args));\n            }\n        }\n\n        var stack = TraceKit.computeStackTrace(ex);\n        lastExceptionStack = stack;\n        lastException = ex;\n\n        // If the stack trace is incomplete, wait for 2 seconds for\n        // slow slow IE to see if onerror occurs or not before reporting\n        // this exception; otherwise, we will end up with an incomplete\n        // stack trace\n        window.setTimeout(function () {\n            if (lastException === ex) {\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [stack, null].concat(args));\n            }\n        }, (stack.incomplete ? 2000 : 0));\n\n        throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n    }\n\n    report.subscribe = subscribe;\n    report.unsubscribe = unsubscribe;\n    return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n *   s = TraceKit.computeStackTrace.ofCaller([depth])\n *   s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n *   s.name              - exception name\n *   s.message           - exception message\n *   s.stack[i].url      - JavaScript or HTML file URL\n *   s.stack[i].func     - function name, or empty for anonymous functions (if guessing did not work)\n *   s.stack[i].args     - arguments passed to the function, if known\n *   s.stack[i].line     - line number, if known\n *   s.stack[i].column   - column number, if known\n *   s.stack[i].context  - an array of source code lines; the middle element corresponds to the correct line#\n *   s.mode              - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n *   - Firefox:  full stack trace with line numbers and unreliable column\n *               number on top frame\n *   - Opera 10: full stack trace with line and column numbers\n *   - Opera 9-: full stack trace with line numbers\n *   - Chrome:   full stack trace with line and column numbers\n *   - Safari:   line and column number for the topmost stacktrace element\n *               only\n *   - IE:       no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n *  * in Safari, some methods may be missing from the stack trace;\n *  * in IE, the topmost function in the stack trace will always be the\n *    caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n *     function trace(message) {\n *         var stackInfo = TraceKit.computeStackTrace.ofCaller();\n *         var data = message + \"\\n\";\n *         for(var i in stackInfo.stack) {\n *             var item = stackInfo.stack[i];\n *             data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n *         }\n *         if (window.console)\n *             console.info(data);\n *         else\n *             alert(data);\n *     }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n    var debug = false,\n        sourceCache = {};\n\n    /**\n     * Attempts to retrieve source code via XMLHttpRequest, which is used\n     * to look up anonymous function names.\n     * @param {string} url URL of source code.\n     * @return {string} Source contents.\n     */\n    function loadSource(url) {\n        if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n            return '';\n        }\n        try {\n            var getXHR = function() {\n                try {\n                    return new window.XMLHttpRequest();\n                } catch (e) {\n                    // explicitly bubble up the exception if not found\n                    return new window.ActiveXObject('Microsoft.XMLHTTP');\n                }\n            };\n\n            var request = getXHR();\n            request.open('GET', url, false);\n            request.send('');\n            return request.responseText;\n        } catch (e) {\n            return '';\n        }\n    }\n\n    /**\n     * Retrieves source code from the source code cache.\n     * @param {string} url URL of source code.\n     * @return {Array.<string>} Source contents.\n     */\n    function getSource(url) {\n        if (typeof url !== 'string') {\n            return [];\n        }\n\n        if (!_has(sourceCache, url)) {\n            // URL needs to be able to fetched within the acceptable domain.  Otherwise,\n            // cross-domain errors will be triggered.\n            var source = '';\n\n            var domain = '';\n            try { domain = document.domain; } catch (e) {}\n            if (url.indexOf(domain) !== -1) {\n                source = loadSource(url);\n            }\n            sourceCache[url] = source ? source.split('\\n') : [];\n        }\n\n        return sourceCache[url];\n    }\n\n    /**\n     * Tries to use an externally loaded copy of source code to determine\n     * the name of a function by looking at the name of the variable it was\n     * assigned to, if any.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} lineNo Line number in source code.\n     * @return {string} The function name, if discoverable.\n     */\n    function guessFunctionName(url, lineNo) {\n        var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n            reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n            line = '',\n            maxLines = 10,\n            source = getSource(url),\n            m;\n\n        if (!source.length) {\n            return UNKNOWN_FUNCTION;\n        }\n\n        // Walk backwards from the first line in the function until we find the line which\n        // matches the pattern above, which is the function definition\n        for (var i = 0; i < maxLines; ++i) {\n            line = source[lineNo - i] + line;\n\n            if (!_isUndefined(line)) {\n                if ((m = reGuessFunction.exec(line))) {\n                    return m[1];\n                } else if ((m = reFunctionArgNames.exec(line))) {\n                    return m[1];\n                }\n            }\n        }\n\n        return UNKNOWN_FUNCTION;\n    }\n\n    /**\n     * Retrieves the surrounding lines from where an exception occurred.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} line Line number in source code to centre\n     * around for context.\n     * @return {?Array.<string>} Lines of source code.\n     */\n    function gatherContext(url, line) {\n        var source = getSource(url);\n\n        if (!source.length) {\n            return null;\n        }\n\n        var context = [],\n            // linesBefore & linesAfter are inclusive with the offending line.\n            // if linesOfContext is even, there will be one extra line\n            //   *before* the offending line.\n            linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n            // Add one extra line if linesOfContext is odd\n            linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n            start = Math.max(0, line - linesBefore - 1),\n            end = Math.min(source.length, line + linesAfter - 1);\n\n        line -= 1; // convert to 0-based index\n\n        for (var i = start; i < end; ++i) {\n            if (!_isUndefined(source[i])) {\n                context.push(source[i]);\n            }\n        }\n\n        return context.length > 0 ? context : null;\n    }\n\n    /**\n     * Escapes special characters, except for whitespace, in a string to be\n     * used inside a regular expression as a string literal.\n     * @param {string} text The string.\n     * @return {string} The escaped string literal.\n     */\n    function escapeRegExp(text) {\n        return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n    }\n\n    /**\n     * Escapes special characters in a string to be used inside a regular\n     * expression as a string literal. Also ensures that HTML entities will\n     * be matched the same as their literal friends.\n     * @param {string} body The string.\n     * @return {string} The escaped string.\n     */\n    function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n        return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('\"', '(?:\"|&quot;)').replace(/\\s+/g, '\\\\s+');\n    }\n\n    /**\n     * Determines where a code fragment occurs in the source code.\n     * @param {RegExp} re The function definition.\n     * @param {Array.<string>} urls A list of URLs to search.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceInUrls(re, urls) {\n        var source, m;\n        for (var i = 0, j = urls.length; i < j; ++i) {\n            // console.log('searching', urls[i]);\n            if ((source = getSource(urls[i])).length) {\n                source = source.join('\\n');\n                if ((m = re.exec(source))) {\n                    // console.log('Found function in ' + urls[i]);\n\n                    return {\n                        'url': urls[i],\n                        'line': source.substring(0, m.index).split('\\n').length,\n                        'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n                    };\n                }\n            }\n        }\n\n        // console.log('no match');\n\n        return null;\n    }\n\n    /**\n     * Determines at which column a code fragment occurs on a line of the\n     * source code.\n     * @param {string} fragment The code fragment.\n     * @param {string} url The URL to search.\n     * @param {(string|number)} line The line number to examine.\n     * @return {?number} The column number.\n     */\n    function findSourceInLine(fragment, url, line) {\n        var source = getSource(url),\n            re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n            m;\n\n        line -= 1;\n\n        if (source && source.length > line && (m = re.exec(source[line]))) {\n            return m.index;\n        }\n\n        return null;\n    }\n\n    /**\n     * Determines where a function was defined within the source code.\n     * @param {(Function|string)} func A function reference or serialized\n     * function definition.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceByFunctionBody(func) {\n        var urls = [window.location.href],\n            scripts = document.getElementsByTagName('script'),\n            body,\n            code = '' + func,\n            codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            re,\n            parts,\n            result;\n\n        for (var i = 0; i < scripts.length; ++i) {\n            var script = scripts[i];\n            if (script.src) {\n                urls.push(script.src);\n            }\n        }\n\n        if (!(parts = codeRE.exec(code))) {\n            re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n        }\n\n        // not sure if this is really necessary, but I don’t have a test\n        // corpus large enough to confirm that and it was in the original.\n        else {\n            var name = parts[1] ? '\\\\s+' + parts[1] : '',\n                args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n            body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n            re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n        }\n\n        // look for a normal function definition\n        if ((result = findSourceInUrls(re, urls))) {\n            return result;\n        }\n\n        // look for an old-school event handler function\n        if ((parts = eventRE.exec(code))) {\n            var event = parts[1];\n            body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n            // look for a function defined in HTML as an onXXX handler\n            re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n            if ((result = findSourceInUrls(re, urls[0]))) {\n                return result;\n            }\n\n            // look for ???\n            re = new RegExp(body);\n\n            if ((result = findSourceInUrls(re, urls))) {\n                return result;\n            }\n        }\n\n        return null;\n    }\n\n    // Contents of Exception in various browsers.\n    //\n    // SAFARI:\n    // ex.message = Can't find variable: qq\n    // ex.line = 59\n    // ex.sourceId = 580238192\n    // ex.sourceURL = http://...\n    // ex.expressionBeginOffset = 96\n    // ex.expressionCaretOffset = 98\n    // ex.expressionEndOffset = 98\n    // ex.name = ReferenceError\n    //\n    // FIREFOX:\n    // ex.message = qq is not defined\n    // ex.fileName = http://...\n    // ex.lineNumber = 59\n    // ex.columnNumber = 69\n    // ex.stack = ...stack trace... (see the example below)\n    // ex.name = ReferenceError\n    //\n    // CHROME:\n    // ex.message = qq is not defined\n    // ex.name = ReferenceError\n    // ex.type = not_defined\n    // ex.arguments = ['aa']\n    // ex.stack = ...stack trace...\n    //\n    // INTERNET EXPLORER:\n    // ex.message = ...\n    // ex.name = ReferenceError\n    //\n    // OPERA:\n    // ex.message = ...message... (see the example below)\n    // ex.name = ReferenceError\n    // ex.opera#sourceloc = 11  (pretty much useless, duplicates the info in ex.message)\n    // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n    /**\n     * Computes stack trace information from the stack property.\n     * Chrome and Gecko use this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStackProp(ex) {\n        if (!ex.stack) {\n            return null;\n        }\n\n        var chrome = /^\\s*at (.*?) ?\\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\\d+))?(?::(\\d+))?\\)?\\s*$/i,\n            gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|chrome|\\[).*?)(?::(\\d+))?(?::(\\d+))?\\s*$/i,\n            winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|http|https):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n            lines = ex.stack.split('\\n'),\n            stack = [],\n            parts,\n            element,\n            reference = /^(.*) is undefined$/.exec(ex.message);\n\n        for (var i = 0, j = lines.length; i < j; ++i) {\n            if ((parts = chrome.exec(lines[i]))) {\n                var isNative = parts[2] && parts[2].indexOf('native') !== -1;\n                element = {\n                    'url': !isNative ? parts[2] : null,\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': isNative ? [parts[2]] : [],\n                    'line': parts[3] ? +parts[3] : null,\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = winjs.exec(lines[i]))) {\n                element = {\n                    'url': parts[2],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': [],\n                    'line': +parts[3],\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = gecko.exec(lines[i]))) {\n                element = {\n                    'url': parts[3],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': parts[2] ? parts[2].split(',') : [],\n                    'line': parts[4] ? +parts[4] : null,\n                    'column': parts[5] ? +parts[5] : null\n                };\n            } else {\n                continue;\n            }\n\n            if (!element.func && element.line) {\n                element.func = guessFunctionName(element.url, element.line);\n            }\n\n            if (element.line) {\n                element.context = gatherContext(element.url, element.line);\n            }\n\n            stack.push(element);\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        if (stack[0] && stack[0].line && !stack[0].column && reference) {\n            stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n        } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n            // FireFox uses this awesome columnNumber property for its top frame\n            // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n            // so adding 1\n            stack[0].column = ex.columnNumber + 1;\n        }\n\n        return {\n            'mode': 'stack',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * Computes stack trace information from the stacktrace property.\n     * Opera 10+ uses this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStacktraceProp(ex) {\n        // Access and store the stacktrace property before doing ANYTHING\n        // else to it because Opera is not very good at providing it\n        // reliably in other circumstances.\n        var stacktrace = ex.stacktrace;\n        if (!stacktrace) {\n            return;\n        }\n\n        var opera10Regex = / line (\\d+).*script (?:in )?(\\S+)(?:: in function (\\S+))?$/i,\n            opera11Regex = / line (\\d+), column (\\d+)\\s*(?:in (?:<anonymous function: ([^>]+)>|([^\\)]+))\\((.*)\\))? in (.*):\\s*$/i,\n            lines = stacktrace.split('\\n'),\n            stack = [],\n            parts;\n\n        for (var line = 0; line < lines.length; line += 2) {\n            var element = null;\n            if ((parts = opera10Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[2],\n                    'line': +parts[1],\n                    'column': null,\n                    'func': parts[3],\n                    'args':[]\n                };\n            } else if ((parts = opera11Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[6],\n                    'line': +parts[1],\n                    'column': +parts[2],\n                    'func': parts[3] || parts[4],\n                    'args': parts[5] ? parts[5].split(',') : []\n                };\n            }\n\n            if (element) {\n                if (!element.func && element.line) {\n                    element.func = guessFunctionName(element.url, element.line);\n                }\n                if (element.line) {\n                    try {\n                        element.context = gatherContext(element.url, element.line);\n                    } catch (exc) {}\n                }\n\n                if (!element.context) {\n                    element.context = [lines[line + 1]];\n                }\n\n                stack.push(element);\n            }\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        return {\n            'mode': 'stacktrace',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * NOT TESTED.\n     * Computes stack trace information from an error message that includes\n     * the stack trace.\n     * Opera 9 and earlier use this method if the option to show stack\n     * traces is turned on in opera:config.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack information.\n     */\n    function computeStackTraceFromOperaMultiLineMessage(ex) {\n        // TODO: Clean this function up\n        // Opera includes a stack trace into the exception message. An example is:\n        //\n        // Statement on line 3: Undefined variable: undefinedFunc\n        // Backtrace:\n        //   Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n        //         undefinedFunc(a);\n        //   Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n        //           zzz(x, y, z);\n        //   Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n        //           yyy(a, a, a);\n        //   Line 1 of function script\n        //     try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n        //   ...\n\n        var lines = ex.message.split('\\n');\n        if (lines.length < 4) {\n            return null;\n        }\n\n        var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n            stack = [],\n            scripts = document.getElementsByTagName('script'),\n            inlineScriptBlocks = [],\n            parts;\n\n        for (var s in scripts) {\n            if (_has(scripts, s) && !scripts[s].src) {\n                inlineScriptBlocks.push(scripts[s]);\n            }\n        }\n\n        for (var line = 2; line < lines.length; line += 2) {\n            var item = null;\n            if ((parts = lineRE1.exec(lines[line]))) {\n                item = {\n                    'url': parts[2],\n                    'func': parts[3],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null\n                };\n            } else if ((parts = lineRE2.exec(lines[line]))) {\n                item = {\n                    'url': parts[3],\n                    'func': parts[4],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.\n                };\n                var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block\n                var script = inlineScriptBlocks[parts[2] - 1];\n                if (script) {\n                    var source = getSource(item.url);\n                    if (source) {\n                        source = source.join('\\n');\n                        var pos = source.indexOf(script.innerText);\n                        if (pos >= 0) {\n                            item.line = relativeLine + source.substring(0, pos).split('\\n').length;\n                        }\n                    }\n                }\n            } else if ((parts = lineRE3.exec(lines[line]))) {\n                var url = window.location.href.replace(/#.*$/, '');\n                var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1]));\n                var src = findSourceInUrls(re, [url]);\n                item = {\n                    'url': url,\n                    'func': '',\n                    'args': [],\n                    'line': src ? src.line : parts[1],\n                    'column': null\n                };\n            }\n\n            if (item) {\n                if (!item.func) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n                var context = gatherContext(item.url, item.line);\n                var midline = (context ? context[Math.floor(context.length / 2)] : null);\n                if (context && midline.replace(/^\\s*/, '') === lines[line + 1].replace(/^\\s*/, '')) {\n                    item.context = context;\n                } else {\n                    // if (context) alert(\"Context mismatch. Correct midline:\\n\" + lines[i+1] + \"\\n\\nMidline:\\n\" + midline + \"\\n\\nContext:\\n\" + context.join(\"\\n\") + \"\\n\\nURL:\\n\" + item.url);\n                    item.context = [lines[line + 1]];\n                }\n                stack.push(item);\n            }\n        }\n        if (!stack.length) {\n            return null; // could not parse multiline exception message as Opera stack trace\n        }\n\n        return {\n            'mode': 'multiline',\n            'name': ex.name,\n            'message': lines[0],\n            'stack': stack\n        };\n    }\n\n    /**\n     * Adds information about the first frame to incomplete stack traces.\n     * Safari and IE require this to get complete data on the first frame.\n     * @param {Object.<string, *>} stackInfo Stack trace information from\n     * one of the compute* methods.\n     * @param {string} url The URL of the script that caused an error.\n     * @param {(number|string)} lineNo The line number of the script that\n     * caused an error.\n     * @param {string=} message The error generated by the browser, which\n     * hopefully contains the name of the object that caused the error.\n     * @return {boolean} Whether or not the stack information was\n     * augmented.\n     */\n    function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {\n        var initial = {\n            'url': url,\n            'line': lineNo\n        };\n\n        if (initial.url && initial.line) {\n            stackInfo.incomplete = false;\n\n            if (!initial.func) {\n                initial.func = guessFunctionName(initial.url, initial.line);\n            }\n\n            if (!initial.context) {\n                initial.context = gatherContext(initial.url, initial.line);\n            }\n\n            var reference = / '([^']+)' /.exec(message);\n            if (reference) {\n                initial.column = findSourceInLine(reference[1], initial.url, initial.line);\n            }\n\n            if (stackInfo.stack.length > 0) {\n                if (stackInfo.stack[0].url === initial.url) {\n                    if (stackInfo.stack[0].line === initial.line) {\n                        return false; // already in stack trace\n                    } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {\n                        stackInfo.stack[0].line = initial.line;\n                        stackInfo.stack[0].context = initial.context;\n                        return false;\n                    }\n                }\n            }\n\n            stackInfo.stack.unshift(initial);\n            stackInfo.partial = true;\n            return true;\n        } else {\n            stackInfo.incomplete = true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Computes stack trace information by walking the arguments.caller\n     * chain at the time the exception occurred. This will cause earlier\n     * frames to be missed but is the only way to get any stack trace in\n     * Safari and IE. The top frame is restored by\n     * {@link augmentStackTraceWithInitialElement}.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceByWalkingCallerChain(ex, depth) {\n        var functionName = /function\\s+([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*)?\\s*\\(/i,\n            stack = [],\n            funcs = {},\n            recursion = false,\n            parts,\n            item,\n            source;\n\n        for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {\n            if (curr === computeStackTrace || curr === TraceKit.report) {\n                // console.log('skipping internal function');\n                continue;\n            }\n\n            item = {\n                'url': null,\n                'func': UNKNOWN_FUNCTION,\n                'args': [],\n                'line': null,\n                'column': null\n            };\n\n            if (curr.name) {\n                item.func = curr.name;\n            } else if ((parts = functionName.exec(curr.toString()))) {\n                item.func = parts[1];\n            }\n\n            if (typeof item.func === 'undefined') {\n              try {\n                item.func = parts.input.substring(0, parts.input.indexOf('{'));\n              } catch (e) { }\n            }\n\n            if ((source = findSourceByFunctionBody(curr))) {\n                item.url = source.url;\n                item.line = source.line;\n\n                if (item.func === UNKNOWN_FUNCTION) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n\n                var reference = / '([^']+)' /.exec(ex.message || ex.description);\n                if (reference) {\n                    item.column = findSourceInLine(reference[1], source.url, source.line);\n                }\n            }\n\n            if (funcs['' + curr]) {\n                recursion = true;\n            }else{\n                funcs['' + curr] = true;\n            }\n\n            stack.push(item);\n        }\n\n        if (depth) {\n            // console.log('depth is ' + depth);\n            // console.log('stack is ' + stack.length);\n            stack.splice(0, depth);\n        }\n\n        var result = {\n            'mode': 'callers',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n        augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);\n        return result;\n    }\n\n    /**\n     * Computes a stack trace for an exception.\n     * @param {Error} ex\n     * @param {(string|number)=} depth\n     */\n    function computeStackTrace(ex, depth) {\n        var stack = null;\n        depth = (depth == null ? 0 : +depth);\n\n        try {\n            // This must be tried first because Opera 10 *destroys*\n            // its stacktrace property if you try to access the stack\n            // property first!!\n            stack = computeStackTraceFromStacktraceProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromStackProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromOperaMultiLineMessage(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        return {\n            'mode': 'failed'\n        };\n    }\n\n    /**\n     * Logs a stacktrace starting from the previous call and working down.\n     * @param {(number|string)=} depth How many frames deep to trace.\n     * @return {Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceOfCaller(depth) {\n        depth = (depth == null ? 0 : +depth) + 1; // \"+ 1\" because \"ofCaller\" should drop one frame\n        try {\n            throw new Error();\n        } catch (ex) {\n            return computeStackTrace(ex, depth + 1);\n        }\n    }\n\n    computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;\n    computeStackTrace.guessFunctionName = guessFunctionName;\n    computeStackTrace.gatherContext = gatherContext;\n    computeStackTrace.ofCaller = computeStackTraceOfCaller;\n    computeStackTrace.getSource = getSource;\n\n    return computeStackTrace;\n}());\n\n/**\n * Extends support for global error handling for asynchronous browser\n * functions. Adopted from Closure Library's errorhandler.js\n */\nTraceKit.extendToAsynchronousCallbacks = function () {\n    var _helper = function _helper(fnName) {\n        var originalFn = window[fnName];\n        window[fnName] = function traceKitAsyncExtension() {\n            // Make a copy of the arguments\n            var args = _slice.call(arguments);\n            var originalCallback = args[0];\n            if (typeof (originalCallback) === 'function') {\n                args[0] = TraceKit.wrap(originalCallback);\n            }\n            // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it\n            // also only supports 2 argument and doesn't care what \"this\" is, so we\n            // can just call the original function directly.\n            if (originalFn.apply) {\n                return originalFn.apply(this, args);\n            } else {\n                return originalFn(args[0], args[1]);\n            }\n        };\n    };\n\n    _helper('setTimeout');\n    _helper('setInterval');\n};\n\n//Default options:\nif (!TraceKit.remoteFetching) {\n  TraceKit.remoteFetching = true;\n}\nif (!TraceKit.collectWindowErrors) {\n  TraceKit.collectWindowErrors = true;\n}\nif (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {\n  // 5 lines before, the offending line, 5 lines after\n  TraceKit.linesOfContext = 11;\n}\n\n\n\n// Export to global object\nwindow.TraceKit = TraceKit;\n\n}(typeof window !== 'undefined' ? window : global));\n","export interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority:number = 30;\n  public name:string = 'ErrorPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\n                                                                                                                                                                                                                                                                     \n\nexport class DefaultErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getParameters(parameters:string|string[]): IParameter[] {\n      let params:string[] = (typeof parameters === 'string' ? [parameters] : parameters) || [];\n\n      let result:IParameter[] = [];\n      for (let index = 0; index < params.length; index++) {\n        result.push({ name: params[index] });\n      }\n\n      return result;\n    }\n\n    function getStackFrames(stackFrames:TraceKit.StackFrame[]): IStackFrame[] {\n      const ANONYMOUS:string = '<anonymous>';\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: (frame.func || ANONYMOUS).replace('?', ANONYMOUS),\n          parameters: getParameters(frame.args),\n          file_name: frame.url,\n          line_number: frame.line || 0,\n          column: frame.column || 0\n        });\n      }\n\n      return frames;\n    }\n\n    const TRACEKIT_STACK_TRACE_KEY:string = '@@_TraceKit.StackTrace'; // optimization for minifier.\n\n    let stackTrace:TraceKit.StackTrace = !!context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      ? context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      : TraceKit.computeStackTrace(exception, 25);\n\n    if (!stackTrace) {\n      throw new Error('Unable to parse the exceptions stack trace.');\n    }\n\n    return {\n      type: stackTrace.name,\n      message: stackTrace.message || exception.message,\n      stack_trace: getStackFrames(stackTrace.stack || [])\n    };\n  }\n}\n\n                                                                                                                                                                                                         \n\nexport class DefaultModuleCollector implements IModuleCollector {\n  public getModules(context:EventPluginContext): IModule[] {\n    if (document && document.getElementsByTagName) {\n      return null;\n    }\n\n    let modules:IModule[] = [];\n    let scripts = document.getElementsByTagName('script');\n    if (scripts && scripts.length > 0) {\n      for (let index = 0; index < scripts.length; index++) {\n        if (scripts[index].src) {\n          modules.push({\n            module_id: index,\n            name: scripts[index].src,\n            version: Utils.parseVersion(scripts[index].src)\n          });\n        } else if (!!scripts[index].innerHTML) {\n          modules.push({\n            module_id: index,\n            name: 'Script Tag',\n            version: Utils.getHashCode(scripts[index].innerHTML)\n          });\n        }\n      }\n    }\n\n    return modules;\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class DefaultRequestInfoCollector implements IRequestInfoCollector {\n  public getRequestInfo(context:EventPluginContext): IRequestInfo {\n    if (!document || !navigator || !location) {\n      return null;\n    }\n\n    let requestInfo:IRequestInfo = {\n      user_agent: navigator.userAgent,\n      is_secure: location.protocol === 'https:',\n      host: location.hostname,\n      port: location.port && location.port !== '' ? parseInt(location.port, 10) : 80,\n      path: location.pathname,\n      // client_ip_address: 'TODO',\n      cookies: Utils.getCookies(document.cookie),\n      query_string: Utils.parseQueryString(location.search.substring(1))\n    };\n\n    if (document.referrer && document.referrer !== '') {\n      requestInfo.referrer = document.referrer;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                              \n\ndeclare var XDomainRequest: { new (); create(); };\n\nexport class DefaultSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request: SubmissionRequest, callback: SubmissionCallback, isAppExiting?:boolean) {\n    // TODO: Handle sending events when app is exiting with send beacon.\n    const TIMEOUT: string = 'timeout';  // optimization for minifier.\n    const LOADED: string = 'loaded';  // optimization for minifier.\n    const WITH_CREDENTIALS: string = 'withCredentials';  // optimization for minifier.\n\n    let isCompleted: boolean = false;\n    let useSetTimeout: boolean = false;\n    function complete(mode: string, xhr: XMLHttpRequest) {\n      function parseResponseHeaders(headerStr) {\n        function trim(value) {\n          return value.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n\n        let headers = {};\n        let headerPairs = (headerStr || '').split('\\u000d\\u000a');\n        for (let index: number = 0; index < headerPairs.length; index++) {\n          let headerPair = headerPairs[index];\n          // Can't use split() here because it does the wrong thing\n          // if the header value has the string \": \" in it.\n          let separator = headerPair.indexOf('\\u003a\\u0020');\n          if (separator > 0) {\n            headers[trim(headerPair.substring(0, separator).toLowerCase())] = headerPair.substring(separator + 2);\n          }\n        }\n\n        return headers;\n      }\n\n      if (isCompleted) {\n        return;\n      }\n\n      isCompleted = true;\n\n      let message: string = xhr.statusText;\n      let responseText: string = xhr.responseText;\n      let status: number = xhr.status;\n\n      if (mode === TIMEOUT || status === 0) {\n        message = 'Unable to connect to server.';\n        status = 0;\n      } else if (mode === LOADED && !status) {\n        status = request.method === 'POST' ? 202 : 200;\n      } else if (status < 200 || status > 299) {\n        let responseBody: any = xhr.responseBody;\n        if (!!responseBody && !!responseBody.message) {\n          message = responseBody.message;\n        } else if (!!responseText && responseText.indexOf('message') !== -1) {\n          try {\n            message = JSON.parse(responseText).message;\n          } catch (e) {\n            message = responseText;\n          }\n        }\n      }\n\n      callback(status || 500, message || '', responseText, parseResponseHeaders(xhr.getAllResponseHeaders && xhr.getAllResponseHeaders()));\n    }\n\n    function createRequest(userAgent:string, method: string, url: string): XMLHttpRequest {\n      let xhr: any = new XMLHttpRequest();\n      if (WITH_CREDENTIALS in xhr) {\n        xhr.open(method, url, true);\n\n        xhr.setRequestHeader('X-Exceptionless-Client', userAgent);\n        if (method === 'POST') {\n          xhr.setRequestHeader('Content-Type', 'application/json');\n        }\n      } else if (typeof XDomainRequest !== 'undefined') {\n        useSetTimeout = true;\n        xhr = new XDomainRequest();\n        xhr.open(method, location.protocol === 'http:' ? url.replace('https:', 'http:') : url);\n      } else {\n        xhr = null;\n      }\n\n      if (xhr) {\n        xhr.timeout = 10000;\n      }\n\n      return xhr;\n    }\n\n    let url = `${request.serverUrl}${request.path}?access_token=${encodeURIComponent(request.apiKey) }`;\n    let xhr = createRequest(request.userAgent, request.method || 'POST', url);\n    if (!xhr) {\n      return callback(503, 'CORS not supported.');\n    }\n\n    if (WITH_CREDENTIALS in xhr) {\n      xhr.onreadystatechange = () => {\n        // xhr not ready.\n        if (xhr.readyState !== 4) {\n          return;\n        }\n\n        complete(LOADED, xhr);\n      };\n    }\n\n    xhr.onprogress = () => {};\n    xhr.ontimeout = () => complete(TIMEOUT, xhr);\n    xhr.onerror = () => complete('error', xhr);\n    xhr.onload = () => complete(LOADED, xhr);\n\n    if (useSetTimeout) {\n      setTimeout(() => xhr.send(request.data), 500);\n    } else {\n      xhr.send(request.data);\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n\nfunction getDefaultsSettingsFromScriptTag(): IConfigurationSettings {\n  if (!document || !document.getElementsByTagName) {\n    return null;\n  }\n\n  let scripts = document.getElementsByTagName('script');\n  for (let index = 0; index < scripts.length; index++) {\n    if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) {\n      return Utils.parseQueryString(scripts[index].src.split('?').pop());\n    }\n  }\n  return null;\n}\n\nfunction processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void {\n  let builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror');\n  builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace;\n  builder.submit();\n}\n\n/*\nTODO: We currently are unable to parse string exceptions.\nfunction processJQueryAjaxError(event, xhr, settings, error:string): void {\n  let client = ExceptionlessClient.default;\n  if (xhr.status === 404) {\n    client.submitNotFound(settings.url);\n  } else if (xhr.status !== 401) {\n    client.createUnhandledException(error, 'JQuery.ajaxError')\n      .setSource(settings.url)\n      .setProperty('status', xhr.status)\n      .setProperty('request', settings.data)\n      .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024))\n      .submit();\n  }\n}\n*/\n\nlet defaults = Configuration.defaults;\nlet settings = getDefaultsSettingsFromScriptTag();\nif (settings && (settings.apiKey || settings.serverUrl)) {\n  defaults.apiKey = settings.apiKey;\n  defaults.serverUrl = settings.serverUrl;\n}\n\ndefaults.errorParser = new DefaultErrorParser();\ndefaults.moduleCollector = new DefaultModuleCollector();\ndefaults.requestInfoCollector = new DefaultRequestInfoCollector();\ndefaults.submissionAdapter = new DefaultSubmissionAdapter();\n\nTraceKit.report.subscribe(processUnhandledException);\nTraceKit.extendToAsynchronousCallbacks();\n\n// window && window.addEventListener && window.addEventListener('beforeunload', function () {\n//   ExceptionlessClient.default.config.queue.process(true);\n// });\n\n// if (typeof $ !== 'undefined' && $(document)) {\n//   $(document).ajaxError(processJQueryAjaxError);\n// }\n\n(<any>Error).stackTraceLimit = Infinity;\n\ndeclare var $;\n\n"],"sourceRoot":"/source/"}
\ No newline at end of file
+{"version":3,"sources":["tracekit.js","/source/exceptionless.ts"],"names":["getDefaultsSettingsFromScriptTag","processUnhandledException"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7oCA,8BAA8B,+BAA+B,CAAC,CAAA;AAqC9D,mCAAmC,+BAA+B,CAAC,CAAA;AACnE,uCAAuC,mCAAmC,CAAC,CAAA;AAC3E,4CAA4C,wCAAwC,CAAC,CAAA;AAIrF,yCAAyC,uCAAuC,CAAC,CAAA;AAKjF,oCAAoC,uBAAuB,CAAC,CAAA;AAC5D,sBAAsB,SAAS,CAAC,CAAA;AAEhC;IACEA,EAAEA,CAACA,CAACA,CAACA,QAAQA,IAAIA,CAACA,QAAQA,CAACA,oBAAoBA,CAACA,CAACA,CAACA;QAChDA,MAAMA,CAACA,IAAIA,CAACA;IACdA,CAACA;IAEDA,IAAIA,OAAOA,GAAGA,QAAQA,CAACA,oBAAoBA,CAACA,QAAQA,CAACA,CAACA;IACtDA,GAAGA,CAACA,CAACA,GAAGA,CAACA,KAAKA,GAAGA,CAACA,EAAEA,KAAKA,GAAGA,OAAOA,CAACA,MAAMA,EAAEA,KAAKA,EAAEA,EAAEA,CAACA;QACpDA,EAAEA,CAACA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,IAAIA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,OAAOA,CAACA,gBAAgBA,CAACA,GAAGA,CAACA,CAACA,CAACA,CAACA,CAACA;YAC5EA,MAAMA,CAACA,aAAKA,CAACA,gBAAgBA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,KAAKA,CAACA,GAAGA,CAACA,CAACA,GAAGA,EAAEA,CAACA,CAACA;QACrEA,CAACA;IACHA,CAACA;IACDA,MAAMA,CAACA,IAAIA,CAACA;AACdA,CAACA;AAED,mCAAmC,UAA8B,EAAE,OAAY;IAC7EC,IAAIA,OAAOA,GAAGA,yCAAmBA,CAACA,OAAOA,CAACA,wBAAwBA,CAACA,IAAIA,KAAKA,CAACA,UAAUA,CAACA,OAAOA,IAAIA,CAACA,OAAOA,IAAIA,EAAEA,CAACA,CAACA,MAAMA,IAAIA,cAAcA,CAACA,EAAEA,SAASA,CAACA,CAACA;IACzJA,OAAOA,CAACA,iBAAiBA,CAACA,wBAAwBA,CAACA,GAAGA,UAAUA,CAACA;IACjEA,OAAOA,CAACA,MAAMA,EAAEA,CAACA;AACnBA,CAACA;AAmBD,IAAI,QAAQ,GAAG,6BAAa,CAAC,QAAQ,CAAC;AACtC,IAAI,QAAQ,GAAG,gCAAgC,EAAE,CAAC;AAClD,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;AAC1C,CAAC;AAED,QAAQ,CAAC,WAAW,GAAG,IAAI,uCAAkB,EAAE,CAAC;AAChD,QAAQ,CAAC,eAAe,GAAG,IAAI,+CAAsB,EAAE,CAAC;AACxD,QAAQ,CAAC,oBAAoB,GAAG,IAAI,yDAA2B,EAAE,CAAC;AAClE,QAAQ,CAAC,iBAAiB,GAAG,IAAI,mDAAwB,EAAE,CAAC;AAE5D,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;AACrD,QAAQ,CAAC,6BAA6B,EAAE,CAAC;AAUnC,KAAM,CAAC,eAAe,GAAG,QAAQ,CAAC","file":"exceptionless.js","sourcesContent":["/*\n TraceKit - Cross browser stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n    return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n    return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n    return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n    window.TraceKit = _oldTraceKit;\n    return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n    function wrapped() {\n        try {\n            return func.apply(this, arguments);\n        } catch (e) {\n            TraceKit.report(e);\n            throw e;\n        }\n    }\n    return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n *   TraceKit.report.subscribe(function(stackInfo) { ... })\n *   TraceKit.report.unsubscribe(function(stackInfo) { ... })\n *   TraceKit.report(exception)\n *   try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n *   - Firefox: full stack trace with line numbers, plus column number\n *              on top frame; column number is not guaranteed\n *   - Opera:   full stack trace with line and column numbers\n *   - Chrome:  full stack trace with line and column numbers\n *   - Safari:  line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *   - IE:      line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n *   - IE5.5+ (only 8.0 tested)\n *   - Firefox 0.9+ (only 3.5+ tested)\n *   - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n *     Exceptions Have Stacktrace to be enabled in opera:config)\n *   - Safari 3+ (only 4+ tested)\n *   - Chrome 1+ (only 5+ tested)\n *   - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n    var handlers = [],\n        lastException = null,\n        lastExceptionStack = null;\n\n    /**\n     * Add a crash handler.\n     * @param {Function} handler\n     */\n    function subscribe(handler) {\n        installGlobalHandler();\n        handlers.push(handler);\n    }\n\n    /**\n     * Remove a crash handler.\n     * @param {Function} handler\n     */\n    function unsubscribe(handler) {\n        for (var i = handlers.length - 1; i >= 0; --i) {\n            if (handlers[i] === handler) {\n                handlers.splice(i, 1);\n            }\n        }\n    }\n\n    /**\n     * Dispatch stack information to all handlers.\n     * @param {Object.<string, *>} stack\n     */\n    function notifyHandlers(stack, isWindowError) {\n        var exception = null;\n        if (isWindowError && !TraceKit.collectWindowErrors) {\n          return;\n        }\n        for (var i in handlers) {\n            if (_has(handlers, i)) {\n                try {\n                    handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n                } catch (inner) {\n                    exception = inner;\n                }\n            }\n        }\n\n        if (exception) {\n            throw exception;\n        }\n    }\n\n    var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n    /**\n     * Ensures all global unhandled exceptions are recorded.\n     * Supported by Gecko and IE.\n     * @param {string} message Error message.\n     * @param {string} url URL of script that generated the exception.\n     * @param {(number|string)} lineNo The line number at which the error\n     * occurred.\n     * @param {?(number|string)} columnNo The column number at which the error\n     * occurred.\n     * @param {?Error} errorObj The actual Error object.\n     */\n    function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n        var stack = null;\n\n        if (errorObj) {\n          stack = TraceKit.computeStackTrace(errorObj);\n        }\n        else\n        {\n            if (lastExceptionStack) {\n                TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n                stack = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n            } else {\n                var location = {\n                    'url': url,\n                    'line': lineNo,\n                    'column': columnNo\n                };\n                location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n                location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n                stack = {\n                    'mode': 'onerror',\n                    'message': message,\n                    'stack': [location]\n                };\n            }\n        }\n\n        notifyHandlers(stack, 'from window.onerror');\n\n        if (_oldOnerrorHandler) {\n            return _oldOnerrorHandler.apply(this, arguments);\n        }\n\n        return false;\n    }\n\n    function installGlobalHandler ()\n    {\n        if (_onErrorHandlerInstalled === true) {\n            return;\n        }\n        _oldOnerrorHandler = window.onerror;\n        window.onerror = traceKitWindowOnError;\n        _onErrorHandlerInstalled = true;\n    }\n\n    /**\n     * Reports an unhandled Error to TraceKit.\n     * @param {Error} ex\n     */\n    function report(ex) {\n        var args = _slice.call(arguments, 1);\n        if (lastExceptionStack) {\n            if (lastException === ex) {\n                return; // already caught by an inner catch block, ignore\n            } else {\n                var s = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [s, null].concat(args));\n            }\n        }\n\n        var stack = TraceKit.computeStackTrace(ex);\n        lastExceptionStack = stack;\n        lastException = ex;\n\n        // If the stack trace is incomplete, wait for 2 seconds for\n        // slow slow IE to see if onerror occurs or not before reporting\n        // this exception; otherwise, we will end up with an incomplete\n        // stack trace\n        window.setTimeout(function () {\n            if (lastException === ex) {\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [stack, null].concat(args));\n            }\n        }, (stack.incomplete ? 2000 : 0));\n\n        throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n    }\n\n    report.subscribe = subscribe;\n    report.unsubscribe = unsubscribe;\n    return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n *   s = TraceKit.computeStackTrace.ofCaller([depth])\n *   s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n *   s.name              - exception name\n *   s.message           - exception message\n *   s.stack[i].url      - JavaScript or HTML file URL\n *   s.stack[i].func     - function name, or empty for anonymous functions (if guessing did not work)\n *   s.stack[i].args     - arguments passed to the function, if known\n *   s.stack[i].line     - line number, if known\n *   s.stack[i].column   - column number, if known\n *   s.stack[i].context  - an array of source code lines; the middle element corresponds to the correct line#\n *   s.mode              - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n *   - Firefox:  full stack trace with line numbers and unreliable column\n *               number on top frame\n *   - Opera 10: full stack trace with line and column numbers\n *   - Opera 9-: full stack trace with line numbers\n *   - Chrome:   full stack trace with line and column numbers\n *   - Safari:   line and column number for the topmost stacktrace element\n *               only\n *   - IE:       no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n *  * in Safari, some methods may be missing from the stack trace;\n *  * in IE, the topmost function in the stack trace will always be the\n *    caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n *     function trace(message) {\n *         var stackInfo = TraceKit.computeStackTrace.ofCaller();\n *         var data = message + \"\\n\";\n *         for(var i in stackInfo.stack) {\n *             var item = stackInfo.stack[i];\n *             data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n *         }\n *         if (window.console)\n *             console.info(data);\n *         else\n *             alert(data);\n *     }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n    var debug = false,\n        sourceCache = {};\n\n    /**\n     * Attempts to retrieve source code via XMLHttpRequest, which is used\n     * to look up anonymous function names.\n     * @param {string} url URL of source code.\n     * @return {string} Source contents.\n     */\n    function loadSource(url) {\n        if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n            return '';\n        }\n        try {\n            var getXHR = function() {\n                try {\n                    return new window.XMLHttpRequest();\n                } catch (e) {\n                    // explicitly bubble up the exception if not found\n                    return new window.ActiveXObject('Microsoft.XMLHTTP');\n                }\n            };\n\n            var request = getXHR();\n            request.open('GET', url, false);\n            request.send('');\n            return request.responseText;\n        } catch (e) {\n            return '';\n        }\n    }\n\n    /**\n     * Retrieves source code from the source code cache.\n     * @param {string} url URL of source code.\n     * @return {Array.<string>} Source contents.\n     */\n    function getSource(url) {\n        if (typeof url !== 'string') {\n            return [];\n        }\n\n        if (!_has(sourceCache, url)) {\n            // URL needs to be able to fetched within the acceptable domain.  Otherwise,\n            // cross-domain errors will be triggered.\n            var source = '';\n\n            var domain = '';\n            try { domain = document.domain; } catch (e) {}\n            if (url.indexOf(domain) !== -1) {\n                source = loadSource(url);\n            }\n            sourceCache[url] = source ? source.split('\\n') : [];\n        }\n\n        return sourceCache[url];\n    }\n\n    /**\n     * Tries to use an externally loaded copy of source code to determine\n     * the name of a function by looking at the name of the variable it was\n     * assigned to, if any.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} lineNo Line number in source code.\n     * @return {string} The function name, if discoverable.\n     */\n    function guessFunctionName(url, lineNo) {\n        var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n            reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n            line = '',\n            maxLines = 10,\n            source = getSource(url),\n            m;\n\n        if (!source.length) {\n            return UNKNOWN_FUNCTION;\n        }\n\n        // Walk backwards from the first line in the function until we find the line which\n        // matches the pattern above, which is the function definition\n        for (var i = 0; i < maxLines; ++i) {\n            line = source[lineNo - i] + line;\n\n            if (!_isUndefined(line)) {\n                if ((m = reGuessFunction.exec(line))) {\n                    return m[1];\n                } else if ((m = reFunctionArgNames.exec(line))) {\n                    return m[1];\n                }\n            }\n        }\n\n        return UNKNOWN_FUNCTION;\n    }\n\n    /**\n     * Retrieves the surrounding lines from where an exception occurred.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} line Line number in source code to centre\n     * around for context.\n     * @return {?Array.<string>} Lines of source code.\n     */\n    function gatherContext(url, line) {\n        var source = getSource(url);\n\n        if (!source.length) {\n            return null;\n        }\n\n        var context = [],\n            // linesBefore & linesAfter are inclusive with the offending line.\n            // if linesOfContext is even, there will be one extra line\n            //   *before* the offending line.\n            linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n            // Add one extra line if linesOfContext is odd\n            linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n            start = Math.max(0, line - linesBefore - 1),\n            end = Math.min(source.length, line + linesAfter - 1);\n\n        line -= 1; // convert to 0-based index\n\n        for (var i = start; i < end; ++i) {\n            if (!_isUndefined(source[i])) {\n                context.push(source[i]);\n            }\n        }\n\n        return context.length > 0 ? context : null;\n    }\n\n    /**\n     * Escapes special characters, except for whitespace, in a string to be\n     * used inside a regular expression as a string literal.\n     * @param {string} text The string.\n     * @return {string} The escaped string literal.\n     */\n    function escapeRegExp(text) {\n        return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n    }\n\n    /**\n     * Escapes special characters in a string to be used inside a regular\n     * expression as a string literal. Also ensures that HTML entities will\n     * be matched the same as their literal friends.\n     * @param {string} body The string.\n     * @return {string} The escaped string.\n     */\n    function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n        return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('\"', '(?:\"|&quot;)').replace(/\\s+/g, '\\\\s+');\n    }\n\n    /**\n     * Determines where a code fragment occurs in the source code.\n     * @param {RegExp} re The function definition.\n     * @param {Array.<string>} urls A list of URLs to search.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceInUrls(re, urls) {\n        var source, m;\n        for (var i = 0, j = urls.length; i < j; ++i) {\n            // console.log('searching', urls[i]);\n            if ((source = getSource(urls[i])).length) {\n                source = source.join('\\n');\n                if ((m = re.exec(source))) {\n                    // console.log('Found function in ' + urls[i]);\n\n                    return {\n                        'url': urls[i],\n                        'line': source.substring(0, m.index).split('\\n').length,\n                        'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n                    };\n                }\n            }\n        }\n\n        // console.log('no match');\n\n        return null;\n    }\n\n    /**\n     * Determines at which column a code fragment occurs on a line of the\n     * source code.\n     * @param {string} fragment The code fragment.\n     * @param {string} url The URL to search.\n     * @param {(string|number)} line The line number to examine.\n     * @return {?number} The column number.\n     */\n    function findSourceInLine(fragment, url, line) {\n        var source = getSource(url),\n            re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n            m;\n\n        line -= 1;\n\n        if (source && source.length > line && (m = re.exec(source[line]))) {\n            return m.index;\n        }\n\n        return null;\n    }\n\n    /**\n     * Determines where a function was defined within the source code.\n     * @param {(Function|string)} func A function reference or serialized\n     * function definition.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceByFunctionBody(func) {\n        var urls = [window.location.href],\n            scripts = document.getElementsByTagName('script'),\n            body,\n            code = '' + func,\n            codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            re,\n            parts,\n            result;\n\n        for (var i = 0; i < scripts.length; ++i) {\n            var script = scripts[i];\n            if (script.src) {\n                urls.push(script.src);\n            }\n        }\n\n        if (!(parts = codeRE.exec(code))) {\n            re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n        }\n\n        // not sure if this is really necessary, but I don’t have a test\n        // corpus large enough to confirm that and it was in the original.\n        else {\n            var name = parts[1] ? '\\\\s+' + parts[1] : '',\n                args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n            body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n            re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n        }\n\n        // look for a normal function definition\n        if ((result = findSourceInUrls(re, urls))) {\n            return result;\n        }\n\n        // look for an old-school event handler function\n        if ((parts = eventRE.exec(code))) {\n            var event = parts[1];\n            body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n            // look for a function defined in HTML as an onXXX handler\n            re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n            if ((result = findSourceInUrls(re, urls[0]))) {\n                return result;\n            }\n\n            // look for ???\n            re = new RegExp(body);\n\n            if ((result = findSourceInUrls(re, urls))) {\n                return result;\n            }\n        }\n\n        return null;\n    }\n\n    // Contents of Exception in various browsers.\n    //\n    // SAFARI:\n    // ex.message = Can't find variable: qq\n    // ex.line = 59\n    // ex.sourceId = 580238192\n    // ex.sourceURL = http://...\n    // ex.expressionBeginOffset = 96\n    // ex.expressionCaretOffset = 98\n    // ex.expressionEndOffset = 98\n    // ex.name = ReferenceError\n    //\n    // FIREFOX:\n    // ex.message = qq is not defined\n    // ex.fileName = http://...\n    // ex.lineNumber = 59\n    // ex.columnNumber = 69\n    // ex.stack = ...stack trace... (see the example below)\n    // ex.name = ReferenceError\n    //\n    // CHROME:\n    // ex.message = qq is not defined\n    // ex.name = ReferenceError\n    // ex.type = not_defined\n    // ex.arguments = ['aa']\n    // ex.stack = ...stack trace...\n    //\n    // INTERNET EXPLORER:\n    // ex.message = ...\n    // ex.name = ReferenceError\n    //\n    // OPERA:\n    // ex.message = ...message... (see the example below)\n    // ex.name = ReferenceError\n    // ex.opera#sourceloc = 11  (pretty much useless, duplicates the info in ex.message)\n    // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n    /**\n     * Computes stack trace information from the stack property.\n     * Chrome and Gecko use this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStackProp(ex) {\n        if (!ex.stack) {\n            return null;\n        }\n\n        var chrome = /^\\s*at (.*?) ?\\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\\d+))?(?::(\\d+))?\\)?\\s*$/i,\n            gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|chrome|\\[).*?)(?::(\\d+))?(?::(\\d+))?\\s*$/i,\n            winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|http|https):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n            lines = ex.stack.split('\\n'),\n            stack = [],\n            parts,\n            element,\n            reference = /^(.*) is undefined$/.exec(ex.message);\n\n        for (var i = 0, j = lines.length; i < j; ++i) {\n            if ((parts = chrome.exec(lines[i]))) {\n                var isNative = parts[2] && parts[2].indexOf('native') !== -1;\n                element = {\n                    'url': !isNative ? parts[2] : null,\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': isNative ? [parts[2]] : [],\n                    'line': parts[3] ? +parts[3] : null,\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = winjs.exec(lines[i]))) {\n                element = {\n                    'url': parts[2],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': [],\n                    'line': +parts[3],\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = gecko.exec(lines[i]))) {\n                element = {\n                    'url': parts[3],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': parts[2] ? parts[2].split(',') : [],\n                    'line': parts[4] ? +parts[4] : null,\n                    'column': parts[5] ? +parts[5] : null\n                };\n            } else {\n                continue;\n            }\n\n            if (!element.func && element.line) {\n                element.func = guessFunctionName(element.url, element.line);\n            }\n\n            if (element.line) {\n                element.context = gatherContext(element.url, element.line);\n            }\n\n            stack.push(element);\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        if (stack[0] && stack[0].line && !stack[0].column && reference) {\n            stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n        } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n            // FireFox uses this awesome columnNumber property for its top frame\n            // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n            // so adding 1\n            stack[0].column = ex.columnNumber + 1;\n        }\n\n        return {\n            'mode': 'stack',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * Computes stack trace information from the stacktrace property.\n     * Opera 10+ uses this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStacktraceProp(ex) {\n        // Access and store the stacktrace property before doing ANYTHING\n        // else to it because Opera is not very good at providing it\n        // reliably in other circumstances.\n        var stacktrace = ex.stacktrace;\n        if (!stacktrace) {\n            return;\n        }\n\n        var opera10Regex = / line (\\d+).*script (?:in )?(\\S+)(?:: in function (\\S+))?$/i,\n            opera11Regex = / line (\\d+), column (\\d+)\\s*(?:in (?:<anonymous function: ([^>]+)>|([^\\)]+))\\((.*)\\))? in (.*):\\s*$/i,\n            lines = stacktrace.split('\\n'),\n            stack = [],\n            parts;\n\n        for (var line = 0; line < lines.length; line += 2) {\n            var element = null;\n            if ((parts = opera10Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[2],\n                    'line': +parts[1],\n                    'column': null,\n                    'func': parts[3],\n                    'args':[]\n                };\n            } else if ((parts = opera11Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[6],\n                    'line': +parts[1],\n                    'column': +parts[2],\n                    'func': parts[3] || parts[4],\n                    'args': parts[5] ? parts[5].split(',') : []\n                };\n            }\n\n            if (element) {\n                if (!element.func && element.line) {\n                    element.func = guessFunctionName(element.url, element.line);\n                }\n                if (element.line) {\n                    try {\n                        element.context = gatherContext(element.url, element.line);\n                    } catch (exc) {}\n                }\n\n                if (!element.context) {\n                    element.context = [lines[line + 1]];\n                }\n\n                stack.push(element);\n            }\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        return {\n            'mode': 'stacktrace',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * NOT TESTED.\n     * Computes stack trace information from an error message that includes\n     * the stack trace.\n     * Opera 9 and earlier use this method if the option to show stack\n     * traces is turned on in opera:config.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack information.\n     */\n    function computeStackTraceFromOperaMultiLineMessage(ex) {\n        // TODO: Clean this function up\n        // Opera includes a stack trace into the exception message. An example is:\n        //\n        // Statement on line 3: Undefined variable: undefinedFunc\n        // Backtrace:\n        //   Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n        //         undefinedFunc(a);\n        //   Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n        //           zzz(x, y, z);\n        //   Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n        //           yyy(a, a, a);\n        //   Line 1 of function script\n        //     try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n        //   ...\n\n        var lines = ex.message.split('\\n');\n        if (lines.length < 4) {\n            return null;\n        }\n\n        var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n            stack = [],\n            scripts = document.getElementsByTagName('script'),\n            inlineScriptBlocks = [],\n            parts;\n\n        for (var s in scripts) {\n            if (_has(scripts, s) && !scripts[s].src) {\n                inlineScriptBlocks.push(scripts[s]);\n            }\n        }\n\n        for (var line = 2; line < lines.length; line += 2) {\n            var item = null;\n            if ((parts = lineRE1.exec(lines[line]))) {\n                item = {\n                    'url': parts[2],\n                    'func': parts[3],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null\n                };\n            } else if ((parts = lineRE2.exec(lines[line]))) {\n                item = {\n                    'url': parts[3],\n                    'func': parts[4],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.\n                };\n                var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block\n                var script = inlineScriptBlocks[parts[2] - 1];\n                if (script) {\n                    var source = getSource(item.url);\n                    if (source) {\n                        source = source.join('\\n');\n                        var pos = source.indexOf(script.innerText);\n                        if (pos >= 0) {\n                            item.line = relativeLine + source.substring(0, pos).split('\\n').length;\n                        }\n                    }\n                }\n            } else if ((parts = lineRE3.exec(lines[line]))) {\n                var url = window.location.href.replace(/#.*$/, '');\n                var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1]));\n                var src = findSourceInUrls(re, [url]);\n                item = {\n                    'url': url,\n                    'func': '',\n                    'args': [],\n                    'line': src ? src.line : parts[1],\n                    'column': null\n                };\n            }\n\n            if (item) {\n                if (!item.func) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n                var context = gatherContext(item.url, item.line);\n                var midline = (context ? context[Math.floor(context.length / 2)] : null);\n                if (context && midline.replace(/^\\s*/, '') === lines[line + 1].replace(/^\\s*/, '')) {\n                    item.context = context;\n                } else {\n                    // if (context) alert(\"Context mismatch. Correct midline:\\n\" + lines[i+1] + \"\\n\\nMidline:\\n\" + midline + \"\\n\\nContext:\\n\" + context.join(\"\\n\") + \"\\n\\nURL:\\n\" + item.url);\n                    item.context = [lines[line + 1]];\n                }\n                stack.push(item);\n            }\n        }\n        if (!stack.length) {\n            return null; // could not parse multiline exception message as Opera stack trace\n        }\n\n        return {\n            'mode': 'multiline',\n            'name': ex.name,\n            'message': lines[0],\n            'stack': stack\n        };\n    }\n\n    /**\n     * Adds information about the first frame to incomplete stack traces.\n     * Safari and IE require this to get complete data on the first frame.\n     * @param {Object.<string, *>} stackInfo Stack trace information from\n     * one of the compute* methods.\n     * @param {string} url The URL of the script that caused an error.\n     * @param {(number|string)} lineNo The line number of the script that\n     * caused an error.\n     * @param {string=} message The error generated by the browser, which\n     * hopefully contains the name of the object that caused the error.\n     * @return {boolean} Whether or not the stack information was\n     * augmented.\n     */\n    function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {\n        var initial = {\n            'url': url,\n            'line': lineNo\n        };\n\n        if (initial.url && initial.line) {\n            stackInfo.incomplete = false;\n\n            if (!initial.func) {\n                initial.func = guessFunctionName(initial.url, initial.line);\n            }\n\n            if (!initial.context) {\n                initial.context = gatherContext(initial.url, initial.line);\n            }\n\n            var reference = / '([^']+)' /.exec(message);\n            if (reference) {\n                initial.column = findSourceInLine(reference[1], initial.url, initial.line);\n            }\n\n            if (stackInfo.stack.length > 0) {\n                if (stackInfo.stack[0].url === initial.url) {\n                    if (stackInfo.stack[0].line === initial.line) {\n                        return false; // already in stack trace\n                    } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {\n                        stackInfo.stack[0].line = initial.line;\n                        stackInfo.stack[0].context = initial.context;\n                        return false;\n                    }\n                }\n            }\n\n            stackInfo.stack.unshift(initial);\n            stackInfo.partial = true;\n            return true;\n        } else {\n            stackInfo.incomplete = true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Computes stack trace information by walking the arguments.caller\n     * chain at the time the exception occurred. This will cause earlier\n     * frames to be missed but is the only way to get any stack trace in\n     * Safari and IE. The top frame is restored by\n     * {@link augmentStackTraceWithInitialElement}.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceByWalkingCallerChain(ex, depth) {\n        var functionName = /function\\s+([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*)?\\s*\\(/i,\n            stack = [],\n            funcs = {},\n            recursion = false,\n            parts,\n            item,\n            source;\n\n        for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {\n            if (curr === computeStackTrace || curr === TraceKit.report) {\n                // console.log('skipping internal function');\n                continue;\n            }\n\n            item = {\n                'url': null,\n                'func': UNKNOWN_FUNCTION,\n                'args': [],\n                'line': null,\n                'column': null\n            };\n\n            if (curr.name) {\n                item.func = curr.name;\n            } else if ((parts = functionName.exec(curr.toString()))) {\n                item.func = parts[1];\n            }\n\n            if (typeof item.func === 'undefined') {\n              try {\n                item.func = parts.input.substring(0, parts.input.indexOf('{'));\n              } catch (e) { }\n            }\n\n            if ((source = findSourceByFunctionBody(curr))) {\n                item.url = source.url;\n                item.line = source.line;\n\n                if (item.func === UNKNOWN_FUNCTION) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n\n                var reference = / '([^']+)' /.exec(ex.message || ex.description);\n                if (reference) {\n                    item.column = findSourceInLine(reference[1], source.url, source.line);\n                }\n            }\n\n            if (funcs['' + curr]) {\n                recursion = true;\n            }else{\n                funcs['' + curr] = true;\n            }\n\n            stack.push(item);\n        }\n\n        if (depth) {\n            // console.log('depth is ' + depth);\n            // console.log('stack is ' + stack.length);\n            stack.splice(0, depth);\n        }\n\n        var result = {\n            'mode': 'callers',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n        augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);\n        return result;\n    }\n\n    /**\n     * Computes a stack trace for an exception.\n     * @param {Error} ex\n     * @param {(string|number)=} depth\n     */\n    function computeStackTrace(ex, depth) {\n        var stack = null;\n        depth = (depth == null ? 0 : +depth);\n\n        try {\n            // This must be tried first because Opera 10 *destroys*\n            // its stacktrace property if you try to access the stack\n            // property first!!\n            stack = computeStackTraceFromStacktraceProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromStackProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromOperaMultiLineMessage(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        return {\n            'mode': 'failed'\n        };\n    }\n\n    /**\n     * Logs a stacktrace starting from the previous call and working down.\n     * @param {(number|string)=} depth How many frames deep to trace.\n     * @return {Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceOfCaller(depth) {\n        depth = (depth == null ? 0 : +depth) + 1; // \"+ 1\" because \"ofCaller\" should drop one frame\n        try {\n            throw new Error();\n        } catch (ex) {\n            return computeStackTrace(ex, depth + 1);\n        }\n    }\n\n    computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;\n    computeStackTrace.guessFunctionName = guessFunctionName;\n    computeStackTrace.gatherContext = gatherContext;\n    computeStackTrace.ofCaller = computeStackTraceOfCaller;\n    computeStackTrace.getSource = getSource;\n\n    return computeStackTrace;\n}());\n\n/**\n * Extends support for global error handling for asynchronous browser\n * functions. Adopted from Closure Library's errorhandler.js\n */\nTraceKit.extendToAsynchronousCallbacks = function () {\n    var _helper = function _helper(fnName) {\n        var originalFn = window[fnName];\n        window[fnName] = function traceKitAsyncExtension() {\n            // Make a copy of the arguments\n            var args = _slice.call(arguments);\n            var originalCallback = args[0];\n            if (typeof (originalCallback) === 'function') {\n                args[0] = TraceKit.wrap(originalCallback);\n            }\n            // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it\n            // also only supports 2 argument and doesn't care what \"this\" is, so we\n            // can just call the original function directly.\n            if (originalFn.apply) {\n                return originalFn.apply(this, args);\n            } else {\n                return originalFn(args[0], args[1]);\n            }\n        };\n    };\n\n    _helper('setTimeout');\n    _helper('setInterval');\n};\n\n//Default options:\nif (!TraceKit.remoteFetching) {\n  TraceKit.remoteFetching = true;\n}\nif (!TraceKit.collectWindowErrors) {\n  TraceKit.collectWindowErrors = true;\n}\nif (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {\n  // 5 lines before, the offending line, 5 lines after\n  TraceKit.linesOfContext = 11;\n}\n\n\n\n// Export to global object\nwindow.TraceKit = TraceKit;\n\n}(typeof window !== 'undefined' ? window : global));\n","export interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority: number = 30;\n  public name: string = 'ErrorPlugin';\n  public ignoredProperties: string[] = [\n    'arguments',\n    'column',\n    'columnNumber',\n    'description',\n    'fileName',\n    'message',\n    'name',\n    'number',\n    'line',\n    'lineNumber',\n    'opera#sourceloc',\n    'sourceURL',\n    'stack',\n    'stacktrace'\n  ];\n\n  public run(context: EventPluginContext, next?: () => void): void {\n    const ERROR_KEY: string = '@error'; // optimization for minifier.\n    const EXTRA_PROPERTIES_KEY: string = '@ext';\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          let additionalData = this.getAdditionalData(exception);\n          if (!!additionalData) {\n            if (!result.data) {\n              result.data = {};\n            }\n            result.data[EXTRA_PROPERTIES_KEY] = additionalData;\n          }\n\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n\n  private getAdditionalData(exception: Error): { [key: string]: any } {\n    let keys = Object.keys(exception)\n      .filter(key => this.ignoredProperties.indexOf(key) < 0);\n\n    if (keys.length === 0) {\n      return null;\n    }\n\n    let additionalData = {};\n\n    keys.forEach(key => {\n      let value = exception[key];\n      if (typeof value !== 'function') {\n        additionalData[key] = value;\n      }\n    });\n\n    return additionalData;\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\n                                                                                                                                                                                                                                                                     \n\nexport class DefaultErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getParameters(parameters:string|string[]): IParameter[] {\n      let params:string[] = (typeof parameters === 'string' ? [parameters] : parameters) || [];\n\n      let result:IParameter[] = [];\n      for (let index = 0; index < params.length; index++) {\n        result.push({ name: params[index] });\n      }\n\n      return result;\n    }\n\n    function getStackFrames(stackFrames:TraceKit.StackFrame[]): IStackFrame[] {\n      const ANONYMOUS:string = '<anonymous>';\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: (frame.func || ANONYMOUS).replace('?', ANONYMOUS),\n          parameters: getParameters(frame.args),\n          file_name: frame.url,\n          line_number: frame.line || 0,\n          column: frame.column || 0\n        });\n      }\n\n      return frames;\n    }\n\n    const TRACEKIT_STACK_TRACE_KEY:string = '@@_TraceKit.StackTrace'; // optimization for minifier.\n\n    let stackTrace:TraceKit.StackTrace = !!context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      ? context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      : TraceKit.computeStackTrace(exception, 25);\n\n    if (!stackTrace) {\n      throw new Error('Unable to parse the exceptions stack trace.');\n    }\n\n    return {\n      type: stackTrace.name,\n      message: stackTrace.message || exception.message,\n      stack_trace: getStackFrames(stackTrace.stack || [])\n    };\n  }\n}\n\n                                                                                                                                                                                                         \n\nexport class DefaultModuleCollector implements IModuleCollector {\n  public getModules(context:EventPluginContext): IModule[] {\n    if (document && document.getElementsByTagName) {\n      return null;\n    }\n\n    let modules:IModule[] = [];\n    let scripts = document.getElementsByTagName('script');\n    if (scripts && scripts.length > 0) {\n      for (let index = 0; index < scripts.length; index++) {\n        if (scripts[index].src) {\n          modules.push({\n            module_id: index,\n            name: scripts[index].src,\n            version: Utils.parseVersion(scripts[index].src)\n          });\n        } else if (!!scripts[index].innerHTML) {\n          modules.push({\n            module_id: index,\n            name: 'Script Tag',\n            version: Utils.getHashCode(scripts[index].innerHTML)\n          });\n        }\n      }\n    }\n\n    return modules;\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class DefaultRequestInfoCollector implements IRequestInfoCollector {\n  public getRequestInfo(context:EventPluginContext): IRequestInfo {\n    if (!document || !navigator || !location) {\n      return null;\n    }\n\n    let requestInfo:IRequestInfo = {\n      user_agent: navigator.userAgent,\n      is_secure: location.protocol === 'https:',\n      host: location.hostname,\n      port: location.port && location.port !== '' ? parseInt(location.port, 10) : 80,\n      path: location.pathname,\n      // client_ip_address: 'TODO',\n      cookies: Utils.getCookies(document.cookie),\n      query_string: Utils.parseQueryString(location.search.substring(1))\n    };\n\n    if (document.referrer && document.referrer !== '') {\n      requestInfo.referrer = document.referrer;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                              \n\ndeclare var XDomainRequest: { new (); create(); };\n\nexport class DefaultSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request: SubmissionRequest, callback: SubmissionCallback, isAppExiting?:boolean) {\n    // TODO: Handle sending events when app is exiting with send beacon.\n    const TIMEOUT: string = 'timeout';  // optimization for minifier.\n    const LOADED: string = 'loaded';  // optimization for minifier.\n    const WITH_CREDENTIALS: string = 'withCredentials';  // optimization for minifier.\n\n    let isCompleted: boolean = false;\n    let useSetTimeout: boolean = false;\n    function complete(mode: string, xhr: XMLHttpRequest) {\n      function parseResponseHeaders(headerStr) {\n        function trim(value) {\n          return value.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n\n        let headers = {};\n        let headerPairs = (headerStr || '').split('\\u000d\\u000a');\n        for (let index: number = 0; index < headerPairs.length; index++) {\n          let headerPair = headerPairs[index];\n          // Can't use split() here because it does the wrong thing\n          // if the header value has the string \": \" in it.\n          let separator = headerPair.indexOf('\\u003a\\u0020');\n          if (separator > 0) {\n            headers[trim(headerPair.substring(0, separator).toLowerCase())] = headerPair.substring(separator + 2);\n          }\n        }\n\n        return headers;\n      }\n\n      if (isCompleted) {\n        return;\n      }\n\n      isCompleted = true;\n\n      let message: string = xhr.statusText;\n      let responseText: string = xhr.responseText;\n      let status: number = xhr.status;\n\n      if (mode === TIMEOUT || status === 0) {\n        message = 'Unable to connect to server.';\n        status = 0;\n      } else if (mode === LOADED && !status) {\n        status = request.method === 'POST' ? 202 : 200;\n      } else if (status < 200 || status > 299) {\n        let responseBody: any = xhr.responseBody;\n        if (!!responseBody && !!responseBody.message) {\n          message = responseBody.message;\n        } else if (!!responseText && responseText.indexOf('message') !== -1) {\n          try {\n            message = JSON.parse(responseText).message;\n          } catch (e) {\n            message = responseText;\n          }\n        }\n      }\n\n      callback(status || 500, message || '', responseText, parseResponseHeaders(xhr.getAllResponseHeaders && xhr.getAllResponseHeaders()));\n    }\n\n    function createRequest(userAgent:string, method: string, url: string): XMLHttpRequest {\n      let xhr: any = new XMLHttpRequest();\n      if (WITH_CREDENTIALS in xhr) {\n        xhr.open(method, url, true);\n\n        xhr.setRequestHeader('X-Exceptionless-Client', userAgent);\n        if (method === 'POST') {\n          xhr.setRequestHeader('Content-Type', 'application/json');\n        }\n      } else if (typeof XDomainRequest !== 'undefined') {\n        useSetTimeout = true;\n        xhr = new XDomainRequest();\n        xhr.open(method, location.protocol === 'http:' ? url.replace('https:', 'http:') : url);\n      } else {\n        xhr = null;\n      }\n\n      if (xhr) {\n        xhr.timeout = 10000;\n      }\n\n      return xhr;\n    }\n\n    let url = `${request.serverUrl}${request.path}?access_token=${encodeURIComponent(request.apiKey) }`;\n    let xhr = createRequest(request.userAgent, request.method || 'POST', url);\n    if (!xhr) {\n      return callback(503, 'CORS not supported.');\n    }\n\n    if (WITH_CREDENTIALS in xhr) {\n      xhr.onreadystatechange = () => {\n        // xhr not ready.\n        if (xhr.readyState !== 4) {\n          return;\n        }\n\n        complete(LOADED, xhr);\n      };\n    }\n\n    xhr.onprogress = () => {};\n    xhr.ontimeout = () => complete(TIMEOUT, xhr);\n    xhr.onerror = () => complete('error', xhr);\n    xhr.onload = () => complete(LOADED, xhr);\n\n    if (useSetTimeout) {\n      setTimeout(() => xhr.send(request.data), 500);\n    } else {\n      xhr.send(request.data);\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n\nfunction getDefaultsSettingsFromScriptTag(): IConfigurationSettings {\n  if (!document || !document.getElementsByTagName) {\n    return null;\n  }\n\n  let scripts = document.getElementsByTagName('script');\n  for (let index = 0; index < scripts.length; index++) {\n    if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) {\n      return Utils.parseQueryString(scripts[index].src.split('?').pop());\n    }\n  }\n  return null;\n}\n\nfunction processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void {\n  let builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror');\n  builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace;\n  builder.submit();\n}\n\n/*\nTODO: We currently are unable to parse string exceptions.\nfunction processJQueryAjaxError(event, xhr, settings, error:string): void {\n  let client = ExceptionlessClient.default;\n  if (xhr.status === 404) {\n    client.submitNotFound(settings.url);\n  } else if (xhr.status !== 401) {\n    client.createUnhandledException(error, 'JQuery.ajaxError')\n      .setSource(settings.url)\n      .setProperty('status', xhr.status)\n      .setProperty('request', settings.data)\n      .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024))\n      .submit();\n  }\n}\n*/\n\nlet defaults = Configuration.defaults;\nlet settings = getDefaultsSettingsFromScriptTag();\nif (settings && (settings.apiKey || settings.serverUrl)) {\n  defaults.apiKey = settings.apiKey;\n  defaults.serverUrl = settings.serverUrl;\n}\n\ndefaults.errorParser = new DefaultErrorParser();\ndefaults.moduleCollector = new DefaultModuleCollector();\ndefaults.requestInfoCollector = new DefaultRequestInfoCollector();\ndefaults.submissionAdapter = new DefaultSubmissionAdapter();\n\nTraceKit.report.subscribe(processUnhandledException);\nTraceKit.extendToAsynchronousCallbacks();\n\n// window && window.addEventListener && window.addEventListener('beforeunload', function () {\n//   ExceptionlessClient.default.config.queue.process(true);\n// });\n\n// if (typeof $ !== 'undefined' && $(document)) {\n//   $(document).ajaxError(processJQueryAjaxError);\n// }\n\n(<any>Error).stackTraceLimit = Infinity;\n\ndeclare var $;\n\n"],"sourceRoot":"/source/"}
\ No newline at end of file
diff --git a/dist/exceptionless.min.js b/dist/exceptionless.min.js
index f81c9a7f..4c33c0db 100644
--- a/dist/exceptionless.min.js
+++ b/dist/exceptionless.min.js
@@ -1,3 +1,3 @@
-!function(e,t){function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function r(e){return"undefined"==typeof e}if(e){var i={},o=e.TraceKit,s=[].slice,u="?";i.noConflict=function(){return e.TraceKit=o,i},i.wrap=function(e){function t(){try{return e.apply(this,arguments)}catch(t){throw i.report(t),t}}return t},i.report=function(){function t(e){a(),p.push(e)}function r(e){for(var t=p.length-1;t>=0;--t)p[t]===e&&p.splice(t,1)}function o(e,t){var r=null;if(!t||i.collectWindowErrors){for(var o in p)if(n(p,o))try{p[o].apply(null,[e].concat(s.call(arguments,2)))}catch(u){r=u}if(r)throw r}}function u(e,t,n,r,s){var u=null;if(s)u=i.computeStackTrace(s);else if(d)i.computeStackTrace.augmentStackTraceWithInitialElement(d,t,n,e),u=d,d=null,g=null;else{var a={url:t,line:n,column:r};a.func=i.computeStackTrace.guessFunctionName(a.url,a.line),a.context=i.computeStackTrace.gatherContext(a.url,a.line),u={mode:"onerror",message:e,stack:[a]}}return o(u,"from window.onerror"),l?l.apply(this,arguments):!1}function a(){f!==!0&&(l=e.onerror,e.onerror=u,f=!0)}function c(t){var n=s.call(arguments,1);if(d){if(g===t)return;var r=d;d=null,g=null,o.apply(null,[r,null].concat(n))}var u=i.computeStackTrace(t);throw d=u,g=t,e.setTimeout(function(){g===t&&(d=null,g=null,o.apply(null,[u,null].concat(n)))},u.incomplete?2e3:0),t}var l,f,p=[],g=null,d=null;return c.subscribe=t,c.unsubscribe=r,c}(),i.computeStackTrace=function(){function t(t){if(!i.remoteFetching)return"";try{var n=function(){try{return new e.XMLHttpRequest}catch(t){return new e.ActiveXObject("Microsoft.XMLHTTP")}},r=n();return r.open("GET",t,!1),r.send(""),r.responseText}catch(o){return""}}function o(e){if("string"!=typeof e)return[];if(!n(E,e)){var r="",i="";try{i=document.domain}catch(o){}-1!==e.indexOf(i)&&(r=t(e)),E[e]=r?r.split("\n"):[]}return E[e]}function s(e,t){var n,i=/function ([^(]*)\(([^)]*)\)/,s=/['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,a="",c=10,l=o(e);if(!l.length)return u;for(var f=0;c>f;++f)if(a=l[t-f]+a,!r(a)){if(n=s.exec(a))return n[1];if(n=i.exec(a))return n[1]}return u}function a(e,t){var n=o(e);if(!n.length)return null;var s=[],u=Math.floor(i.linesOfContext/2),a=u+i.linesOfContext%2,c=Math.max(0,t-u-1),l=Math.min(n.length,t+a-1);t-=1;for(var f=c;l>f;++f)r(n[f])||s.push(n[f]);return s.length>0?s:null}function c(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g,"\\$&")}function l(e){return c(e).replace("<","(?:<|&lt;)").replace(">","(?:>|&gt;)").replace("&","(?:&|&amp;)").replace('"','(?:"|&quot;)').replace(/\s+/g,"\\s+")}function f(e,t){for(var n,r,i=0,s=t.length;s>i;++i)if((n=o(t[i])).length&&(n=n.join("\n"),r=e.exec(n)))return{url:t[i],line:n.substring(0,r.index).split("\n").length,column:r.index-n.lastIndexOf("\n",r.index)-1};return null}function p(e,t,n){var r,i=o(t),s=new RegExp("\\b"+c(e)+"\\b");return n-=1,i&&i.length>n&&(r=s.exec(i[n]))?r.index:null}function g(t){for(var n,r,i,o,s=[e.location.href],u=document.getElementsByTagName("script"),a=""+t,p=/^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,g=/^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,d=0;d<u.length;++d){var h=u[d];h.src&&s.push(h.src)}if(i=p.exec(a)){var v=i[1]?"\\s+"+i[1]:"",m=i[2].split(",").join("\\s*,\\s*");n=c(i[3]).replace(/;$/,";?"),r=new RegExp("function"+v+"\\s*\\(\\s*"+m+"\\s*\\)\\s*{\\s*"+n+"\\s*}")}else r=new RegExp(c(a).replace(/\s+/g,"\\s+"));if(o=f(r,s))return o;if(i=g.exec(a)){var y=i[1];if(n=l(i[2]),r=new RegExp("on"+y+"=[\\'\"]\\s*"+n+"\\s*[\\'\"]","i"),o=f(r,s[0]))return o;if(r=new RegExp(n),o=f(r,s))return o}return null}function d(e){if(!e.stack)return null;for(var t,n,i=/^\s*at (.*?) ?\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,o=/^\s*(.*?)(?:\((.*?)\))?@?((?:file|https?|chrome|\[).*?)(?::(\d+))?(?::(\d+))?\s*$/i,c=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,l=e.stack.split("\n"),f=[],g=/^(.*) is undefined$/.exec(e.message),d=0,h=l.length;h>d;++d){if(t=i.exec(l[d])){var v=t[2]&&-1!==t[2].indexOf("native");n={url:v?null:t[2],func:t[1]||u,args:v?[t[2]]:[],line:t[3]?+t[3]:null,column:t[4]?+t[4]:null}}else if(t=c.exec(l[d]))n={url:t[2],func:t[1]||u,args:[],line:+t[3],column:t[4]?+t[4]:null};else{if(!(t=o.exec(l[d])))continue;n={url:t[3],func:t[1]||u,args:t[2]?t[2].split(","):[],line:t[4]?+t[4]:null,column:t[5]?+t[5]:null}}!n.func&&n.line&&(n.func=s(n.url,n.line)),n.line&&(n.context=a(n.url,n.line)),f.push(n)}return f.length?(f[0]&&f[0].line&&!f[0].column&&g?f[0].column=p(g[1],f[0].url,f[0].line):f[0].column||r(e.columnNumber)||(f[0].column=e.columnNumber+1),{mode:"stack",name:e.name,message:e.message,stack:f}):null}function h(e){var t=e.stacktrace;if(t){for(var n,r=/ line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i,i=/ line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i,o=t.split("\n"),u=[],c=0;c<o.length;c+=2){var l=null;if((n=r.exec(o[c]))?l={url:n[2],line:+n[1],column:null,func:n[3],args:[]}:(n=i.exec(o[c]))&&(l={url:n[6],line:+n[1],column:+n[2],func:n[3]||n[4],args:n[5]?n[5].split(","):[]}),l){if(!l.func&&l.line&&(l.func=s(l.url,l.line)),l.line)try{l.context=a(l.url,l.line)}catch(f){}l.context||(l.context=[o[c+1]]),u.push(l)}}return u.length?{mode:"stacktrace",name:e.name,message:e.message,stack:u}:null}}function v(t){var r=t.message.split("\n");if(r.length<4)return null;var i,u=/^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,c=/^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,p=/^\s*Line (\d+) of function script\s*$/i,g=[],d=document.getElementsByTagName("script"),h=[];for(var v in d)n(d,v)&&!d[v].src&&h.push(d[v]);for(var m=2;m<r.length;m+=2){var y=null;if(i=u.exec(r[m]))y={url:i[2],func:i[3],args:[],line:+i[1],column:null};else if(i=c.exec(r[m])){y={url:i[3],func:i[4],args:[],line:+i[1],column:null};var b=+i[1],x=h[i[2]-1];if(x){var S=o(y.url);if(S){S=S.join("\n");var E=S.indexOf(x.innerText);E>=0&&(y.line=b+S.substring(0,E).split("\n").length)}}}else if(i=p.exec(r[m])){var w=e.location.href.replace(/#.*$/,""),_=new RegExp(l(r[m+1])),T=f(_,[w]);y={url:w,func:"",args:[],line:T?T.line:i[1],column:null}}if(y){y.func||(y.func=s(y.url,y.line));var P=a(y.url,y.line),I=P?P[Math.floor(P.length/2)]:null;P&&I.replace(/^\s*/,"")===r[m+1].replace(/^\s*/,"")?y.context=P:y.context=[r[m+1]],g.push(y)}}return g.length?{mode:"multiline",name:t.name,message:r[0],stack:g}:null}function m(e,t,n,r){var i={url:t,line:n};if(i.url&&i.line){e.incomplete=!1,i.func||(i.func=s(i.url,i.line)),i.context||(i.context=a(i.url,i.line));var o=/ '([^']+)' /.exec(r);if(o&&(i.column=p(o[1],i.url,i.line)),e.stack.length>0&&e.stack[0].url===i.url){if(e.stack[0].line===i.line)return!1;if(!e.stack[0].line&&e.stack[0].func===i.func)return e.stack[0].line=i.line,e.stack[0].context=i.context,!1}return e.stack.unshift(i),e.partial=!0,!0}return e.incomplete=!0,!1}function y(e,t){for(var n,r,o,a=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,c=[],l={},f=!1,d=y.caller;d&&!f;d=d.caller)if(d!==b&&d!==i.report){if(r={url:null,func:u,args:[],line:null,column:null},d.name?r.func=d.name:(n=a.exec(d.toString()))&&(r.func=n[1]),"undefined"==typeof r.func)try{r.func=n.input.substring(0,n.input.indexOf("{"))}catch(h){}if(o=g(d)){r.url=o.url,r.line=o.line,r.func===u&&(r.func=s(r.url,r.line));var v=/ '([^']+)' /.exec(e.message||e.description);v&&(r.column=p(v[1],o.url,o.line))}l[""+d]?f=!0:l[""+d]=!0,c.push(r)}t&&c.splice(0,t);var x={mode:"callers",name:e.name,message:e.message,stack:c};return m(x,e.sourceURL||e.fileName,e.line||e.lineNumber,e.message||e.description),x}function b(e,t){var n=null;t=null==t?0:+t;try{if(n=h(e))return n}catch(r){if(S)throw r}try{if(n=d(e))return n}catch(r){if(S)throw r}try{if(n=v(e))return n}catch(r){if(S)throw r}try{if(n=y(e,t+1))return n}catch(r){if(S)throw r}return{mode:"failed"}}function x(e){e=(null==e?0:+e)+1;try{throw new Error}catch(t){return b(t,e+1)}}var S=!1,E={};return b.augmentStackTraceWithInitialElement=m,b.guessFunctionName=s,b.gatherContext=a,b.ofCaller=x,b.getSource=o,b}(),i.extendToAsynchronousCallbacks=function(){var t=function(t){var n=e[t];e[t]=function(){var e=s.call(arguments),t=e[0];return"function"==typeof t&&(e[0]=i.wrap(t)),n.apply?n.apply(this,e):n(e[0],e[1])}};t("setTimeout"),t("setInterval")},i.remoteFetching||(i.remoteFetching=!0),i.collectWindowErrors||(i.collectWindowErrors=!0),(!i.linesOfContext||i.linesOfContext<1)&&(i.linesOfContext=11),e.TraceKit=i}}("undefined"!=typeof window?window:global),function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t(require,exports,module):e.exceptionless=t()}(this,function(e,t,n){function r(){if(!document||!document.getElementsByTagName)return null;for(var e=document.getElementsByTagName("script"),t=0;t<e.length;t++)if(e[t].src&&e[t].src.indexOf("/exceptionless")>-1)return h.parseQueryString(e[t].src.split("?").pop());return null}function i(e,t){var n=x["default"].createUnhandledException(new Error(e.message||(t||{}).status||"Script error"),"onerror");n.pluginContextData["@@_TraceKit.StackTrace"]=e,n.submit()}if(!t)var t={};var o=function(){function e(){}return e.onChanged=function(e){!!e&&this._handlers.push(e)},e.applySavedServerSettings=function(e){e.log.info("Applying saved settings."),e.settings=h.merge(e.settings,this.getSavedServerSettings(e)),this.changed(e)},e.checkVersion=function(e,t){if(e){var n=parseInt(t.storage.get(this._configPath+"-version"),10);(isNaN(n)||e>n)&&(t.log.info("Updating settings from v"+(isNaN(n)?0:n)+" to v"+e),this.updateSettings(t))}},e.updateSettings=function(t){var n=this;return t.isValid?void t.submissionClient.getSettings(t,function(r){if(r&&r.success&&r.settings){t.settings=h.merge(t.settings,r.settings);var i=e.getSavedServerSettings(t);for(var o in i)r.settings[o]||delete t.settings[o];var s=e._configPath;t.storage.save(s+"-version",r.settingsVersion),t.storage.save(s,r.settings),t.log.info("Updated settings"),n.changed(t)}}):void t.log.error("Unable to update settings: ApiKey is not set.")},e.changed=function(e){for(var t=this._handlers,n=0;n<t.length;n++)t[n](e)},e.getSavedServerSettings=function(e){return e.storage.get(this._configPath)||{}},e._configPath="ex-server-settings.json",e._handlers=[],e}();t.SettingsManager=o;var s=function(){function e(){this._lastReferenceId=null}return e.prototype.getLast=function(){return this._lastReferenceId},e.prototype.clearLast=function(){this._lastReferenceId=null},e.prototype.setLast=function(e){this._lastReferenceId=e},e}();t.DefaultLastReferenceIdManager=s;var u=function(){function e(){}return e.prototype.info=function(e){this.log("info",e)},e.prototype.warn=function(e){this.log("warn",e)},e.prototype.error=function(e){this.log("error",e)},e.prototype.log=function(e,t){console&&console[e]&&console[e]("["+e+"] Exceptionless: "+t)},e}();t.ConsoleLog=u;var a=function(){function e(){}return e.prototype.info=function(e){},e.prototype.warn=function(e){},e.prototype.error=function(e){},e}();t.NullLog=a;var c=function(){function e(e,t,n){this.client=e,this.event=t,this.contextData=n?n:new y}return Object.defineProperty(e.prototype,"log",{get:function(){return this.client.config.log},enumerable:!0,configurable:!0}),e}();t.EventPluginContext=c;var l=function(){function e(){}return e.run=function(e,t){var n=function(n,r){return function(){try{e.cancelled||n.run(e,r)}catch(i){e.cancelled=!0,e.log.error("Error running plugin '"+n.name+"': "+i.message+". Discarding Event.")}e.cancelled&&t&&t(e)}},r=e.client.config.plugins,i=[];t&&(i[r.length]=n({name:"cb",priority:9007199254740992,run:t},null));for(var o=r.length-1;o>-1;o--)i[o]=n(r[o],t||o<r.length-1?i[o+1]:null);i[0]()},e.addDefaultPlugins=function(e){e.addPlugin(new S),e.addPlugin(new E),e.addPlugin(new w),e.addPlugin(new _),e.addPlugin(new T),e.addPlugin(new P)},e}();t.EventPluginManager=l;var f=function(){function e(){this.priority=20,this.name="ReferenceIdPlugin"}return e.prototype.run=function(e,t){e.event.reference_id&&0!==e.event.reference_id.length||"error"!==e.event.type||(e.event.reference_id=h.guid().replace("-","").substring(0,10)),t&&t()},e}();t.ReferenceIdPlugin=f;var p=function(){function e(e){this._processingQueue=!1,this._config=e}return e.prototype.enqueue=function(e){var t=this._config;if(this.ensureQueueTimer(),this.areQueuedItemsDiscarded())return void t.log.info("Queue items are currently being discarded. The event will not be queued.");var n="ex-q-"+(new Date).toJSON()+"-"+h.randomNumber();t.log.info("Enqueuing event: "+n+" type="+e.type+" "+(e.reference_id?"refid="+e.reference_id:"")),t.storage.save(n,e)},e.prototype.process=function(e){function t(e){for(var t=[],n=0;n<e.length;n++)t.push(e[n].value);return t}var n=this,r="The queue will not be processed.",i=this._config,o=i.log;if(this.ensureQueueTimer(),!this._processingQueue){if(o.info("Processing queue..."),!i.enabled)return void o.info("Configuration is disabled. "+r);if(!i.isValid)return void o.info("Invalid Api Key. "+r);this._processingQueue=!0;try{var s=i.storage.getList("ex-q",i.submissionBatchSize);if(!s||0===s.length)return void(this._processingQueue=!1);o.info("Sending "+s.length+" events to "+i.serverUrl+"."),i.submissionClient.postEvents(t(s),i,function(e){n.processSubmissionResponse(e,s),o.info("Finished processing queue."),n._processingQueue=!1},e)}catch(u){o.error("Error processing queue: "+u),this.suspendProcessing(),this._processingQueue=!1}}},e.prototype.suspendProcessing=function(e,t,n){var r=this._config;(!e||0>=e)&&(e=5),r.log.info("Suspending processing for "+e+" minutes."),this._suspendProcessingUntil=new Date((new Date).getTime()+6e4*e),t&&(this._discardQueuedItemsUntil=new Date((new Date).getTime()+6e4*e)),n&&this.removeEvents(r.storage.getList("ex-q"))},e.prototype.areQueuedItemsDiscarded=function(){return this._discardQueuedItemsUntil&&this._discardQueuedItemsUntil>new Date},e.prototype.ensureQueueTimer=function(){var e=this;this._queueTimer||(this._queueTimer=setInterval(function(){return e.onProcessQueue()},1e4))},e.prototype.isQueueProcessingSuspended=function(){return this._suspendProcessingUntil&&this._suspendProcessingUntil>new Date},e.prototype.onProcessQueue=function(){this.isQueueProcessingSuspended()||this._processingQueue||this.process()},e.prototype.processSubmissionResponse=function(e,t){var n="The event will not be submitted.",r=this._config,i=r.log;if(e.success)return i.info("Sent "+t.length+" events."),void this.removeEvents(t);if(e.serviceUnavailable)return i.error("Server returned service unavailable."),void this.suspendProcessing();if(e.paymentRequired)return i.info("Too many events have been submitted, please upgrade your plan."),void this.suspendProcessing(null,!0,!0);if(e.unableToAuthenticate)return i.info("Unable to authenticate, please check your configuration. "+n),this.suspendProcessing(15),void this.removeEvents(t);if(e.notFound||e.badRequest)return i.error("Error while trying to submit data: "+e.message),this.suspendProcessing(240),void this.removeEvents(t);if(e.requestEntityTooLarge){var o="Event submission discarded for being too large.";return void(r.submissionBatchSize>1?(i.error(o+" Retrying with smaller batch size."),r.submissionBatchSize=Math.max(1,Math.round(r.submissionBatchSize/1.5))):(i.error(o+" "+n),this.removeEvents(t)))}e.success||(i.error("Error submitting events: "+(e.message||"Please check the network tab for more info.")),this.suspendProcessing())},e.prototype.removeEvents=function(e){for(var t=0;t<(e||[]).length;t++)this._config.storage.remove(e[t].path)},e}();t.DefaultEventQueue=p;var g=function(){function e(e){this._items=[],this._maxItems=e>0?e:250}return e.prototype.save=function(e,t){return e&&t?(this.remove(e),this._items.push({created:(new Date).getTime(),path:e,value:t})>this._maxItems&&this._items.shift(),!0):!1},e.prototype.get=function(e){var t=e?this.getList("^"+e+"$",1)[0]:null;return t?t.value:null},e.prototype.getList=function(e,t){var n=this._items;if(!e)return n.slice(0,t);for(var r=new RegExp(e),i=[],o=0;o<n.length&&!(r.test(n[o].path)&&(i.push(n[o]),i.length>=t));o++);return i},e.prototype.remove=function(e){if(e){var t=this.getList("^"+e+"$",1)[0];t&&this._items.splice(this._items.indexOf(t),1)}},e}();t.InMemoryStorage=g;var d=function(){function e(){this.configurationVersionHeader="x-exceptionless-configversion"}return e.prototype.postEvents=function(e,t,n,r){var i=h.stringify(e,t.dataExclusions),o=this.createRequest(t,"POST","/api/v2/events",i),s=this.createSubmissionCallback(t,n);return t.submissionAdapter.sendRequest(o,s,r)},e.prototype.postUserDescription=function(e,t,n,r){var i="/api/v2/events/by-ref/"+encodeURIComponent(e)+"/user-description",o=h.stringify(t,n.dataExclusions),s=this.createRequest(n,"POST",i,o),u=this.createSubmissionCallback(n,r);return n.submissionAdapter.sendRequest(s,u)},e.prototype.getSettings=function(e,t){var n=this.createRequest(e,"GET","/api/v2/projects/config"),r=function(n,r,i,o){if(200!==n)return t(new I(!1,null,-1,null,r));var s;try{s=JSON.parse(i)}catch(u){e.log.error("Unable to parse settings: '"+i+"'")}return!s||isNaN(s.version)?t(new I(!1,null,-1,null,"Invalid configuration settings.")):void t(new I(!0,s.settings||{},s.version))};return e.submissionAdapter.sendRequest(n,r)},e.prototype.createRequest=function(e,t,n,r){return void 0===r&&(r=null),{method:t,path:n,data:r,serverUrl:e.serverUrl,apiKey:e.apiKey,userAgent:e.userAgent}},e.prototype.createSubmissionCallback=function(e,t){var n=this;return function(r,i,s,u){var a=u&&parseInt(u[n.configurationVersionHeader],10);o.checkVersion(a,e),t(new b(r,i))}},e}();t.DefaultSubmissionClient=d;var h=function(){function e(){}return e.addRange=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];if(e||(e=[]),!t||0===t.length)return e;for(var r=0;r<t.length;r++)t[r]&&e.indexOf(t[r])<0&&e.push(t[r]);return e},e.getHashCode=function(e){if(!e||0===e.length)return null;for(var t=0,n=0;n<e.length;n++){var r=e.charCodeAt(n);t=(t<<5)-t+r,t|=0}return t.toString()},e.getCookies=function(e){for(var t={},n=(e||"").split("; "),r=0;r<n.length;r++){var i=n[r].split("=");t[i[0]]=i[1]}return t},e.guid=function(){function e(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e()},e.merge=function(e,t){var n={};for(var r in e||{})e[r]&&(n[r]=e[r]);for(var r in t||{})t[r]&&(n[r]=t[r]);return n},e.parseVersion=function(e){if(!e)return null;var t=/(v?((\d+)\.(\d+)(\.(\d+))?)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?)/,n=t.exec(e);return n&&n.length>0?n[0]:null},e.parseQueryString=function(e){if(!e||0===e.length)return null;var t=e.split("&");if(0===t.length)return null;for(var n={},r=0;r<t.length;r++){var i=t[r].split("=");n[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return n},e.randomNumber=function(){return Math.floor(9007199254740992*Math.random())},e.stringify=function(e,t){function n(e,t){if(!e||!t||"string"!=typeof t)return!1;var n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;if(e=e.toLowerCase().replace(n,""),t=t.toLowerCase().replace(n,""),e.length<=0)return!1;var r="*"===e[0];r&&(e=e.slice(1));var i="*"===e[e.length-1];return i&&(e=e.substring(0,e.length-1)),r&&i?-1!==t.indexOf(e):r?t.lastIndexOf(e)===t.length-e.length:i?0===t.indexOf(e):t===e}function r(e,t){var r=[];return JSON.stringify(e,function(e,i){for(var o=0;o<(t||[]).length;o++)if(n(t[o],e))return;if("object"==typeof i&&i){if(-1!==r.indexOf(i))return;r.push(i)}return i})}if("[object Array]"==={}.toString.call(e)){for(var i=[],o=0;o<e.length;o++)i[o]=JSON.parse(r(e[o],t||[]));return JSON.stringify(i)}return r(e,t||[])},e}();t.Utils=h;var v=function(){function e(t){function n(e){return"function"==typeof e?e(this):e}this.defaultTags=[],this.defaultData={},this.enabled=!0,this.lastReferenceIdManager=new s,this.settings={},this._plugins=[],this._serverUrl="https://collector.exceptionless.io",this._dataExclusions=[],t=h.merge(e.defaults,t),this.log=n(t.log)||new a,this.apiKey=t.apiKey,this.serverUrl=t.serverUrl,this.environmentInfoCollector=n(t.environmentInfoCollector),this.errorParser=n(t.errorParser),this.lastReferenceIdManager=n(t.lastReferenceIdManager)||new s,this.moduleCollector=n(t.moduleCollector),this.requestInfoCollector=n(t.requestInfoCollector),this.submissionBatchSize=n(t.submissionBatchSize)||50,this.submissionAdapter=n(t.submissionAdapter),this.submissionClient=n(t.submissionClient)||new d,this.storage=n(t.storage)||new g,this.queue=n(t.queue)||new p(this),o.applySavedServerSettings(this),l.addDefaultPlugins(this)}return Object.defineProperty(e.prototype,"apiKey",{get:function(){return this._apiKey},set:function(e){this._apiKey=e||null,this.log.info("apiKey: "+this._apiKey)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isValid",{get:function(){return!!this.apiKey&&this.apiKey.length>=10},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"serverUrl",{get:function(){return this._serverUrl},set:function(e){e&&(this._serverUrl=e,this.log.info("serverUrl: "+this._serverUrl))},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"dataExclusions",{get:function(){var e=this.settings["@@DataExclusions"];return this._dataExclusions.concat(e&&e.split(",")||[])},enumerable:!0,configurable:!0}),e.prototype.addDataExclusions=function(){for(var e=[],t=0;t<arguments.length;t++)e[t-0]=arguments[t];this._dataExclusions=h.addRange.apply(h,[this._dataExclusions].concat(e))},Object.defineProperty(e.prototype,"plugins",{get:function(){return this._plugins.sort(function(e,t){return e.priority<t.priority?-1:e.priority>t.priority?1:0})},enumerable:!0,configurable:!0}),e.prototype.addPlugin=function(e,t,n){var r=n?{name:e,priority:t,run:n}:e;if(!r||!r.run)return void this.log.error("Add plugin failed: Run method not defined");r.name||(r.name=h.guid()),r.priority||(r.priority=0);for(var i=!1,o=this._plugins,s=0;s<o.length;s++)if(o[s].name===r.name){i=!0;break}i||o.push(r)},e.prototype.removePlugin=function(e){var t="string"==typeof e?e:e.name;if(!t)return void this.log.error("Remove plugin failed: Plugin name not defined");for(var n=this._plugins,r=0;r<n.length;r++)if(n[r].name===t){n.splice(r,1);break}},e.prototype.setVersion=function(e){e&&(this.defaultData["@version"]=e)},e.prototype.setUserIdentity=function(e,t){var n="@user",r="string"!=typeof e?e:{identity:e,name:t},i=!r||!r.identity&&!r.name;i?delete this.defaultData[n]:this.defaultData[n]=r,this.log.info("user identity: "+(i?"null":r.identity))},Object.defineProperty(e.prototype,"userAgent",{get:function(){return"exceptionless-js/1.1.1"},enumerable:!0,configurable:!0}),e.prototype.useReferenceIds=function(){this.addPlugin(new f)},e.prototype.useDebugLogger=function(){this.log=new u},Object.defineProperty(e,"defaults",{get:function(){return null===e._defaultSettings&&(e._defaultSettings={}),e._defaultSettings},enumerable:!0,configurable:!0}),e._defaultSettings=null,e}();t.Configuration=v;var m=function(){function e(e,t,n){this._validIdentifierErrorMessage="must contain between 8 and 100 alphanumeric or '-' characters.",this.target=e,this.client=t,this.pluginContextData=n||new y}return e.prototype.setType=function(e){return e&&(this.target.type=e),this},e.prototype.setSource=function(e){return e&&(this.target.source=e),this},e.prototype.setSessionId=function(e){if(!this.isValidIdentifier(e))throw new Error("SessionId "+this._validIdentifierErrorMessage);return this.target.session_id=e,this},e.prototype.setReferenceId=function(e){if(!this.isValidIdentifier(e))throw new Error("ReferenceId "+this._validIdentifierErrorMessage);return this.target.reference_id=e,this},e.prototype.setMessage=function(e){return e&&(this.target.message=e),this},e.prototype.setGeo=function(e,t){if(-90>e||e>90)throw new Error("Must be a valid latitude value between -90.0 and 90.0.");if(-180>t||t>180)throw new Error("Must be a valid longitude value between -180.0 and 180.0.");return this.target.geo=e+","+t,this},e.prototype.setUserIdentity=function(e,t){var n="string"!=typeof e?e:{identity:e,name:t};return n&&(n.identity||n.name)?(this.setProperty("@user",n),this):this},e.prototype.setValue=function(e){return e&&(this.target.value=e),this},e.prototype.addTags=function(){for(var e=[],t=0;t<arguments.length;t++)e[t-0]=arguments[t];return this.target.tags=h.addRange.apply(h,[this.target.tags].concat(e)),this},e.prototype.setProperty=function(e,t){return e&&void 0!==t&&null!=t?(this.target.data||(this.target.data={}),this.target.data[e]=t,this):this},e.prototype.markAsCritical=function(e){return e&&this.addTags("Critical"),this},e.prototype.addRequestInfo=function(e){return e&&(this.pluginContextData["@request"]=e),this},e.prototype.submit=function(e){this.client.submitEvent(this.target,this.pluginContextData,e)},e.prototype.isValidIdentifier=function(e){if(!e)return!0;if(e.length<8||e.length>100)return!1;for(var t=0;t<e.length;t++){var n=e.charCodeAt(t),r=n>=48&&57>=n,i=n>=65&&90>=n||n>=97&&122>=n,o=45===n;if(!r&&!i&&!o)return!1}return!0},e}();t.EventBuilder=m;var y=function(){function e(){}return e.prototype.setException=function(e){e&&(this["@@_Exception"]=e)},Object.defineProperty(e.prototype,"hasException",{get:function(){return!!this["@@_Exception"]},enumerable:!0,configurable:!0}),e.prototype.getException=function(){return this["@@_Exception"]||null},e.prototype.markAsUnhandledError=function(){this["@@_IsUnhandledError"]=!0},Object.defineProperty(e.prototype,"isUnhandledError",{get:function(){return!!this["@@_IsUnhandledError"]},enumerable:!0,configurable:!0}),e.prototype.setSubmissionMethod=function(e){e&&(this["@@_SubmissionMethod"]=e)},e.prototype.getSubmissionMethod=function(){return this["@@_SubmissionMethod"]||null},e}();t.ContextData=y;var b=function(){function e(e,t){this.success=!1,this.badRequest=!1,this.serviceUnavailable=!1,this.paymentRequired=!1,this.unableToAuthenticate=!1,this.notFound=!1,this.requestEntityTooLarge=!1,this.statusCode=e,this.message=t,this.success=e>=200&&299>=e,this.badRequest=400===e,this.serviceUnavailable=503===e,this.paymentRequired=402===e,this.unableToAuthenticate=401===e||403===e,this.notFound=404===e,this.requestEntityTooLarge=413===e}return e}();t.SubmissionResponse=b;var x=function(){function e(e,t){"object"!=typeof e?this.config=new v(e):this.config=new v({apiKey:e,serverUrl:t})}return e.prototype.createException=function(e){var t=new y;return t.setException(e),this.createEvent(t).setType("error")},e.prototype.submitException=function(e,t){this.createException(e).submit(t)},e.prototype.createUnhandledException=function(e,t){var n=this.createException(e);return n.pluginContextData.markAsUnhandledError(),n.pluginContextData.setSubmissionMethod(t),n},e.prototype.submitUnhandledException=function(e,t,n){this.createUnhandledException(e,t).submit(n)},e.prototype.createFeatureUsage=function(e){return this.createEvent().setType("usage").setSource(e)},e.prototype.submitFeatureUsage=function(e,t){this.createFeatureUsage(e).submit(t)},e.prototype.createLog=function(e,t,n){var r=this.createEvent().setType("log");if(t&&n)r=r.setSource(e).setMessage(t).setProperty("@level",n);else if(t)r=r.setSource(e).setMessage(t);else{var i=arguments.callee.caller;r=r.setSource(i&&i.name).setMessage(e)}return r},e.prototype.submitLog=function(e,t,n,r){this.createLog(e,t,n).submit(r)},e.prototype.createNotFound=function(e){return this.createEvent().setType("404").setSource(e)},e.prototype.submitNotFound=function(e,t){this.createNotFound(e).submit(t)},e.prototype.createSessionStart=function(e){return this.createEvent().setType("start").setSessionId(e)},e.prototype.submitSessionStart=function(e,t){this.createSessionStart(e).submit(t)},e.prototype.createSessionEnd=function(e){return this.createEvent().setType("end").setSessionId(e)},e.prototype.submitSessionEnd=function(e,t){this.createSessionEnd(e).submit(t)},e.prototype.createEvent=function(e){return new m({date:new Date},this,e)},e.prototype.submitEvent=function(e,t,n){function r(e){return e&&(e.cancelled=!0),!!n&&n(e)}var i=new c(this,e,t);return e?this.config.enabled?(e.data||(e.data={}),e.tags&&e.tags.length||(e.tags=[]),void l.run(i,function(e){var t=e.event;if(!e.cancelled){t.type&&0!==t.type.length||(t.type="log"),t.date||(t.date=new Date);var r=e.client.config;r.queue.enqueue(t),t.reference_id&&t.reference_id.length>0&&(e.log.info("Setting last reference id '"+t.reference_id+"'"),r.lastReferenceIdManager.setLast(t.reference_id))}!!n&&n(e)})):(this.config.log.info("Event submission is currently disabled."),r(i)):r(i)},e.prototype.updateUserEmailAndDescription=function(e,t,n,r){var i=this;if(!(e&&t&&n&&this.config.enabled))return!!r&&r(new b(500,"cancelled"));var o={email_address:t,description:n};this.config.submissionClient.postUserDescription(e,o,this.config,function(t){t.success||i.config.log.error("Failed to submit user email and description for event '"+e+"': "+t.statusCode+" "+t.message),!!r&&r(t)})},e.prototype.getLastReferenceId=function(){return this.config.lastReferenceIdManager.getLast()},Object.defineProperty(e,"default",{get:function(){return null===e._instance&&(e._instance=new e(null)),e._instance},enumerable:!0,configurable:!0}),e._instance=null,e}();t.ExceptionlessClient=x;var S=function(){function e(){this.priority=10,this.name="ConfigurationDefaultsPlugin"}return e.prototype.run=function(e,t){for(var n=e.client.config.defaultTags||[],r=0;r<n.length;r++){var i=n[r];i&&e.event.tags.indexOf(i)<0&&e.event.tags.push(i)}var o=e.client.config.defaultData||{};for(var s in o)o[s]&&(e.event.data[s]=o[s]);t&&t()},e}();t.ConfigurationDefaultsPlugin=S;var E=function(){function e(){this.priority=30,this.name="ErrorPlugin"}return e.prototype.run=function(e,t){var n="@error",r=e.contextData.getException();if(r&&(e.event.type="error",!e.event.data[n])){var i=e.client.config.errorParser;if(!i)throw new Error("No error parser was defined.");var o=i.parse(e,r);o&&(e.event.data[n]=o)}t&&t()},e}();t.ErrorPlugin=E;var w=function(){function e(){this.priority=40,this.name="ModuleInfoPlugin"}return e.prototype.run=function(e,t){var n="@error",r=e.client.config.moduleCollector;if(e.event.data[n]&&!e.event.data["@error"].modules&&r){var i=r.getModules(e);i&&i.length>0&&(e.event.data[n].modules=i)}t&&t()},e}();t.ModuleInfoPlugin=w;var _=function(){function e(){this.priority=60,this.name="RequestInfoPlugin"}return e.prototype.run=function(e,t){var n="@request",r=e.client.config.requestInfoCollector;if(!e.event.data[n]&&r){var i=r.getRequestInfo(e);i&&(e.event.data[n]=i)}t&&t()},e}();t.RequestInfoPlugin=_;var T=function(){function e(){this.priority=70,this.name="EnvironmentInfoPlugin"}return e.prototype.run=function(e,t){var n="@environment",r=e.client.config.environmentInfoCollector;if(!e.event.data[n]&&r){var i=r.getEnvironmentInfo(e);i&&(e.event.data[n]=i)}t&&t()},e}();t.EnvironmentInfoPlugin=T;var P=function(){function e(){this.priority=100,this.name="SubmissionMethodPlugin"}return e.prototype.run=function(e,t){var n=e.contextData.getSubmissionMethod();n&&(e.event.data["@submission_method"]=n),t&&t()},e}();t.SubmissionMethodPlugin=P;var I=function(){function e(e,t,n,r,i){void 0===n&&(n=-1),void 0===r&&(r=null),void 0===i&&(i=null),this.success=!1,this.settingsVersion=-1,this.success=e,this.settings=t,this.settingsVersion=n,this.exception=r,this.message=i}return e}();t.SettingsResponse=I;var C=function(){function e(){}return e.prototype.parse=function(e,t){function n(e){for(var t=("string"==typeof e?[e]:e)||[],n=[],r=0;r<t.length;r++)n.push({name:t[r]});return n}function r(e){for(var t="<anonymous>",r=[],i=0;i<e.length;i++){var o=e[i];r.push({name:(o.func||t).replace("?",t),parameters:n(o.args),file_name:o.url,line_number:o.line||0,column:o.column||0})}return r}var i="@@_TraceKit.StackTrace",o=e.contextData[i]?e.contextData[i]:TraceKit.computeStackTrace(t,25);
-if(!o)throw new Error("Unable to parse the exceptions stack trace.");return{type:o.name,message:o.message||t.message,stack_trace:r(o.stack||[])}},e}();t.DefaultErrorParser=C;var R=function(){function e(){}return e.prototype.getModules=function(e){if(document&&document.getElementsByTagName)return null;var t=[],n=document.getElementsByTagName("script");if(n&&n.length>0)for(var r=0;r<n.length;r++)n[r].src?t.push({module_id:r,name:n[r].src,version:h.parseVersion(n[r].src)}):n[r].innerHTML&&t.push({module_id:r,name:"Script Tag",version:h.getHashCode(n[r].innerHTML)});return t},e}();t.DefaultModuleCollector=R;var U=function(){function e(){}return e.prototype.getRequestInfo=function(e){if(!document||!navigator||!location)return null;var t={user_agent:navigator.userAgent,is_secure:"https:"===location.protocol,host:location.hostname,port:location.port&&""!==location.port?parseInt(location.port,10):80,path:location.pathname,cookies:h.getCookies(document.cookie),query_string:h.parseQueryString(location.search.substring(1))};return document.referrer&&""!==document.referrer&&(t.referrer=document.referrer),t},e}();t.DefaultRequestInfoCollector=U;var k=function(){function e(){}return e.prototype.sendRequest=function(e,t,n){function r(n,r){function i(e){function t(e){return e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}for(var n={},r=(e||"").split("\r\n"),i=0;i<r.length;i++){var o=r[i],s=o.indexOf(": ");s>0&&(n[t(o.substring(0,s).toLowerCase())]=o.substring(s+2))}return n}if(!a){a=!0;var u=r.statusText,c=r.responseText,l=r.status;if(n===o||0===l)u="Unable to connect to server.",l=0;else if(n!==s||l){if(200>l||l>299){var f=r.responseBody;if(f&&f.message)u=f.message;else if(c&&-1!==c.indexOf("message"))try{u=JSON.parse(c).message}catch(p){u=c}}}else l="POST"===e.method?202:200;t(l||500,u||"",c,i(r.getAllResponseHeaders&&r.getAllResponseHeaders()))}}function i(e,t,n){var r=new XMLHttpRequest;return u in r?(r.open(t,n,!0),r.setRequestHeader("X-Exceptionless-Client",e),"POST"===t&&r.setRequestHeader("Content-Type","application/json")):"undefined"!=typeof XDomainRequest?(c=!0,r=new XDomainRequest,r.open(t,"http:"===location.protocol?n.replace("https:","http:"):n)):r=null,r&&(r.timeout=1e4),r}var o="timeout",s="loaded",u="withCredentials",a=!1,c=!1,l=""+e.serverUrl+e.path+"?access_token="+encodeURIComponent(e.apiKey),f=i(e.userAgent,e.method||"POST",l);return f?(u in f&&(f.onreadystatechange=function(){4===f.readyState&&r(s,f)}),f.onprogress=function(){},f.ontimeout=function(){return r(o,f)},f.onerror=function(){return r("error",f)},f.onload=function(){return r(s,f)},void(c?setTimeout(function(){return f.send(e.data)},500):f.send(e.data))):t(503,"CORS not supported.")},e}();t.DefaultSubmissionAdapter=k;var q=v.defaults,D=r();return D&&(D.apiKey||D.serverUrl)&&(q.apiKey=D.apiKey,q.serverUrl=D.serverUrl),q.errorParser=new C,q.moduleCollector=new R,q.requestInfoCollector=new U,q.submissionAdapter=new k,TraceKit.report.subscribe(i),TraceKit.extendToAsynchronousCallbacks(),Error.stackTraceLimit=1/0,t});
+!function(e,t){function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function r(e){return"undefined"==typeof e}if(e){var i={},o=e.TraceKit,s=[].slice,u="?";i.noConflict=function(){return e.TraceKit=o,i},i.wrap=function(e){function t(){try{return e.apply(this,arguments)}catch(t){throw i.report(t),t}}return t},i.report=function(){function t(e){a(),p.push(e)}function r(e){for(var t=p.length-1;t>=0;--t)p[t]===e&&p.splice(t,1)}function o(e,t){var r=null;if(!t||i.collectWindowErrors){for(var o in p)if(n(p,o))try{p[o].apply(null,[e].concat(s.call(arguments,2)))}catch(u){r=u}if(r)throw r}}function u(e,t,n,r,s){var u=null;if(s)u=i.computeStackTrace(s);else if(d)i.computeStackTrace.augmentStackTraceWithInitialElement(d,t,n,e),u=d,d=null,g=null;else{var a={url:t,line:n,column:r};a.func=i.computeStackTrace.guessFunctionName(a.url,a.line),a.context=i.computeStackTrace.gatherContext(a.url,a.line),u={mode:"onerror",message:e,stack:[a]}}return o(u,"from window.onerror"),l?l.apply(this,arguments):!1}function a(){f!==!0&&(l=e.onerror,e.onerror=u,f=!0)}function c(t){var n=s.call(arguments,1);if(d){if(g===t)return;var r=d;d=null,g=null,o.apply(null,[r,null].concat(n))}var u=i.computeStackTrace(t);throw d=u,g=t,e.setTimeout(function(){g===t&&(d=null,g=null,o.apply(null,[u,null].concat(n)))},u.incomplete?2e3:0),t}var l,f,p=[],g=null,d=null;return c.subscribe=t,c.unsubscribe=r,c}(),i.computeStackTrace=function(){function t(t){if(!i.remoteFetching)return"";try{var n=function(){try{return new e.XMLHttpRequest}catch(t){return new e.ActiveXObject("Microsoft.XMLHTTP")}},r=n();return r.open("GET",t,!1),r.send(""),r.responseText}catch(o){return""}}function o(e){if("string"!=typeof e)return[];if(!n(E,e)){var r="",i="";try{i=document.domain}catch(o){}-1!==e.indexOf(i)&&(r=t(e)),E[e]=r?r.split("\n"):[]}return E[e]}function s(e,t){var n,i=/function ([^(]*)\(([^)]*)\)/,s=/['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,a="",c=10,l=o(e);if(!l.length)return u;for(var f=0;c>f;++f)if(a=l[t-f]+a,!r(a)){if(n=s.exec(a))return n[1];if(n=i.exec(a))return n[1]}return u}function a(e,t){var n=o(e);if(!n.length)return null;var s=[],u=Math.floor(i.linesOfContext/2),a=u+i.linesOfContext%2,c=Math.max(0,t-u-1),l=Math.min(n.length,t+a-1);t-=1;for(var f=c;l>f;++f)r(n[f])||s.push(n[f]);return s.length>0?s:null}function c(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g,"\\$&")}function l(e){return c(e).replace("<","(?:<|&lt;)").replace(">","(?:>|&gt;)").replace("&","(?:&|&amp;)").replace('"','(?:"|&quot;)').replace(/\s+/g,"\\s+")}function f(e,t){for(var n,r,i=0,s=t.length;s>i;++i)if((n=o(t[i])).length&&(n=n.join("\n"),r=e.exec(n)))return{url:t[i],line:n.substring(0,r.index).split("\n").length,column:r.index-n.lastIndexOf("\n",r.index)-1};return null}function p(e,t,n){var r,i=o(t),s=new RegExp("\\b"+c(e)+"\\b");return n-=1,i&&i.length>n&&(r=s.exec(i[n]))?r.index:null}function g(t){for(var n,r,i,o,s=[e.location.href],u=document.getElementsByTagName("script"),a=""+t,p=/^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,g=/^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,d=0;d<u.length;++d){var h=u[d];h.src&&s.push(h.src)}if(i=p.exec(a)){var v=i[1]?"\\s+"+i[1]:"",m=i[2].split(",").join("\\s*,\\s*");n=c(i[3]).replace(/;$/,";?"),r=new RegExp("function"+v+"\\s*\\(\\s*"+m+"\\s*\\)\\s*{\\s*"+n+"\\s*}")}else r=new RegExp(c(a).replace(/\s+/g,"\\s+"));if(o=f(r,s))return o;if(i=g.exec(a)){var y=i[1];if(n=l(i[2]),r=new RegExp("on"+y+"=[\\'\"]\\s*"+n+"\\s*[\\'\"]","i"),o=f(r,s[0]))return o;if(r=new RegExp(n),o=f(r,s))return o}return null}function d(e){if(!e.stack)return null;for(var t,n,i=/^\s*at (.*?) ?\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,o=/^\s*(.*?)(?:\((.*?)\))?@?((?:file|https?|chrome|\[).*?)(?::(\d+))?(?::(\d+))?\s*$/i,c=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,l=e.stack.split("\n"),f=[],g=/^(.*) is undefined$/.exec(e.message),d=0,h=l.length;h>d;++d){if(t=i.exec(l[d])){var v=t[2]&&-1!==t[2].indexOf("native");n={url:v?null:t[2],func:t[1]||u,args:v?[t[2]]:[],line:t[3]?+t[3]:null,column:t[4]?+t[4]:null}}else if(t=c.exec(l[d]))n={url:t[2],func:t[1]||u,args:[],line:+t[3],column:t[4]?+t[4]:null};else{if(!(t=o.exec(l[d])))continue;n={url:t[3],func:t[1]||u,args:t[2]?t[2].split(","):[],line:t[4]?+t[4]:null,column:t[5]?+t[5]:null}}!n.func&&n.line&&(n.func=s(n.url,n.line)),n.line&&(n.context=a(n.url,n.line)),f.push(n)}return f.length?(f[0]&&f[0].line&&!f[0].column&&g?f[0].column=p(g[1],f[0].url,f[0].line):f[0].column||r(e.columnNumber)||(f[0].column=e.columnNumber+1),{mode:"stack",name:e.name,message:e.message,stack:f}):null}function h(e){var t=e.stacktrace;if(t){for(var n,r=/ line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i,i=/ line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i,o=t.split("\n"),u=[],c=0;c<o.length;c+=2){var l=null;if((n=r.exec(o[c]))?l={url:n[2],line:+n[1],column:null,func:n[3],args:[]}:(n=i.exec(o[c]))&&(l={url:n[6],line:+n[1],column:+n[2],func:n[3]||n[4],args:n[5]?n[5].split(","):[]}),l){if(!l.func&&l.line&&(l.func=s(l.url,l.line)),l.line)try{l.context=a(l.url,l.line)}catch(f){}l.context||(l.context=[o[c+1]]),u.push(l)}}return u.length?{mode:"stacktrace",name:e.name,message:e.message,stack:u}:null}}function v(t){var r=t.message.split("\n");if(r.length<4)return null;var i,u=/^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,c=/^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,p=/^\s*Line (\d+) of function script\s*$/i,g=[],d=document.getElementsByTagName("script"),h=[];for(var v in d)n(d,v)&&!d[v].src&&h.push(d[v]);for(var m=2;m<r.length;m+=2){var y=null;if(i=u.exec(r[m]))y={url:i[2],func:i[3],args:[],line:+i[1],column:null};else if(i=c.exec(r[m])){y={url:i[3],func:i[4],args:[],line:+i[1],column:null};var b=+i[1],x=h[i[2]-1];if(x){var S=o(y.url);if(S){S=S.join("\n");var E=S.indexOf(x.innerText);E>=0&&(y.line=b+S.substring(0,E).split("\n").length)}}}else if(i=p.exec(r[m])){var w=e.location.href.replace(/#.*$/,""),_=new RegExp(l(r[m+1])),T=f(_,[w]);y={url:w,func:"",args:[],line:T?T.line:i[1],column:null}}if(y){y.func||(y.func=s(y.url,y.line));var P=a(y.url,y.line),I=P?P[Math.floor(P.length/2)]:null;P&&I.replace(/^\s*/,"")===r[m+1].replace(/^\s*/,"")?y.context=P:y.context=[r[m+1]],g.push(y)}}return g.length?{mode:"multiline",name:t.name,message:r[0],stack:g}:null}function m(e,t,n,r){var i={url:t,line:n};if(i.url&&i.line){e.incomplete=!1,i.func||(i.func=s(i.url,i.line)),i.context||(i.context=a(i.url,i.line));var o=/ '([^']+)' /.exec(r);if(o&&(i.column=p(o[1],i.url,i.line)),e.stack.length>0&&e.stack[0].url===i.url){if(e.stack[0].line===i.line)return!1;if(!e.stack[0].line&&e.stack[0].func===i.func)return e.stack[0].line=i.line,e.stack[0].context=i.context,!1}return e.stack.unshift(i),e.partial=!0,!0}return e.incomplete=!0,!1}function y(e,t){for(var n,r,o,a=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,c=[],l={},f=!1,d=y.caller;d&&!f;d=d.caller)if(d!==b&&d!==i.report){if(r={url:null,func:u,args:[],line:null,column:null},d.name?r.func=d.name:(n=a.exec(d.toString()))&&(r.func=n[1]),"undefined"==typeof r.func)try{r.func=n.input.substring(0,n.input.indexOf("{"))}catch(h){}if(o=g(d)){r.url=o.url,r.line=o.line,r.func===u&&(r.func=s(r.url,r.line));var v=/ '([^']+)' /.exec(e.message||e.description);v&&(r.column=p(v[1],o.url,o.line))}l[""+d]?f=!0:l[""+d]=!0,c.push(r)}t&&c.splice(0,t);var x={mode:"callers",name:e.name,message:e.message,stack:c};return m(x,e.sourceURL||e.fileName,e.line||e.lineNumber,e.message||e.description),x}function b(e,t){var n=null;t=null==t?0:+t;try{if(n=h(e))return n}catch(r){if(S)throw r}try{if(n=d(e))return n}catch(r){if(S)throw r}try{if(n=v(e))return n}catch(r){if(S)throw r}try{if(n=y(e,t+1))return n}catch(r){if(S)throw r}return{mode:"failed"}}function x(e){e=(null==e?0:+e)+1;try{throw new Error}catch(t){return b(t,e+1)}}var S=!1,E={};return b.augmentStackTraceWithInitialElement=m,b.guessFunctionName=s,b.gatherContext=a,b.ofCaller=x,b.getSource=o,b}(),i.extendToAsynchronousCallbacks=function(){var t=function(t){var n=e[t];e[t]=function(){var e=s.call(arguments),t=e[0];return"function"==typeof t&&(e[0]=i.wrap(t)),n.apply?n.apply(this,e):n(e[0],e[1])}};t("setTimeout"),t("setInterval")},i.remoteFetching||(i.remoteFetching=!0),i.collectWindowErrors||(i.collectWindowErrors=!0),(!i.linesOfContext||i.linesOfContext<1)&&(i.linesOfContext=11),e.TraceKit=i}}("undefined"!=typeof window?window:global),function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t(require,exports,module):e.exceptionless=t()}(this,function(e,t,n){function r(){if(!document||!document.getElementsByTagName)return null;for(var e=document.getElementsByTagName("script"),t=0;t<e.length;t++)if(e[t].src&&e[t].src.indexOf("/exceptionless")>-1)return h.parseQueryString(e[t].src.split("?").pop());return null}function i(e,t){var n=x["default"].createUnhandledException(new Error(e.message||(t||{}).status||"Script error"),"onerror");n.pluginContextData["@@_TraceKit.StackTrace"]=e,n.submit()}if(!t)var t={};var o=function(){function e(){}return e.onChanged=function(e){!!e&&this._handlers.push(e)},e.applySavedServerSettings=function(e){e.log.info("Applying saved settings."),e.settings=h.merge(e.settings,this.getSavedServerSettings(e)),this.changed(e)},e.checkVersion=function(e,t){if(e){var n=parseInt(t.storage.get(this._configPath+"-version"),10);(isNaN(n)||e>n)&&(t.log.info("Updating settings from v"+(isNaN(n)?0:n)+" to v"+e),this.updateSettings(t))}},e.updateSettings=function(t){var n=this;return t.isValid?void t.submissionClient.getSettings(t,function(r){if(r&&r.success&&r.settings){t.settings=h.merge(t.settings,r.settings);var i=e.getSavedServerSettings(t);for(var o in i)r.settings[o]||delete t.settings[o];var s=e._configPath;t.storage.save(s+"-version",r.settingsVersion),t.storage.save(s,r.settings),t.log.info("Updated settings"),n.changed(t)}}):void t.log.error("Unable to update settings: ApiKey is not set.")},e.changed=function(e){for(var t=this._handlers,n=0;n<t.length;n++)t[n](e)},e.getSavedServerSettings=function(e){return e.storage.get(this._configPath)||{}},e._configPath="ex-server-settings.json",e._handlers=[],e}();t.SettingsManager=o;var s=function(){function e(){this._lastReferenceId=null}return e.prototype.getLast=function(){return this._lastReferenceId},e.prototype.clearLast=function(){this._lastReferenceId=null},e.prototype.setLast=function(e){this._lastReferenceId=e},e}();t.DefaultLastReferenceIdManager=s;var u=function(){function e(){}return e.prototype.info=function(e){this.log("info",e)},e.prototype.warn=function(e){this.log("warn",e)},e.prototype.error=function(e){this.log("error",e)},e.prototype.log=function(e,t){console&&console[e]&&console[e]("["+e+"] Exceptionless: "+t)},e}();t.ConsoleLog=u;var a=function(){function e(){}return e.prototype.info=function(e){},e.prototype.warn=function(e){},e.prototype.error=function(e){},e}();t.NullLog=a;var c=function(){function e(e,t,n){this.client=e,this.event=t,this.contextData=n?n:new y}return Object.defineProperty(e.prototype,"log",{get:function(){return this.client.config.log},enumerable:!0,configurable:!0}),e}();t.EventPluginContext=c;var l=function(){function e(){}return e.run=function(e,t){var n=function(n,r){return function(){try{e.cancelled||n.run(e,r)}catch(i){e.cancelled=!0,e.log.error("Error running plugin '"+n.name+"': "+i.message+". Discarding Event.")}e.cancelled&&t&&t(e)}},r=e.client.config.plugins,i=[];t&&(i[r.length]=n({name:"cb",priority:9007199254740992,run:t},null));for(var o=r.length-1;o>-1;o--)i[o]=n(r[o],t||o<r.length-1?i[o+1]:null);i[0]()},e.addDefaultPlugins=function(e){e.addPlugin(new S),e.addPlugin(new E),e.addPlugin(new w),e.addPlugin(new _),e.addPlugin(new T),e.addPlugin(new P)},e}();t.EventPluginManager=l;var f=function(){function e(){this.priority=20,this.name="ReferenceIdPlugin"}return e.prototype.run=function(e,t){e.event.reference_id&&0!==e.event.reference_id.length||"error"!==e.event.type||(e.event.reference_id=h.guid().replace("-","").substring(0,10)),t&&t()},e}();t.ReferenceIdPlugin=f;var p=function(){function e(e){this._processingQueue=!1,this._config=e}return e.prototype.enqueue=function(e){var t=this._config;if(this.ensureQueueTimer(),this.areQueuedItemsDiscarded())return void t.log.info("Queue items are currently being discarded. The event will not be queued.");var n="ex-q-"+(new Date).toJSON()+"-"+h.randomNumber();t.log.info("Enqueuing event: "+n+" type="+e.type+" "+(e.reference_id?"refid="+e.reference_id:"")),t.storage.save(n,e)},e.prototype.process=function(e){function t(e){for(var t=[],n=0;n<e.length;n++)t.push(e[n].value);return t}var n=this,r="The queue will not be processed.",i=this._config,o=i.log;if(this.ensureQueueTimer(),!this._processingQueue){if(o.info("Processing queue..."),!i.enabled)return void o.info("Configuration is disabled. "+r);if(!i.isValid)return void o.info("Invalid Api Key. "+r);this._processingQueue=!0;try{var s=i.storage.getList("ex-q",i.submissionBatchSize);if(!s||0===s.length)return void(this._processingQueue=!1);o.info("Sending "+s.length+" events to "+i.serverUrl+"."),i.submissionClient.postEvents(t(s),i,function(e){n.processSubmissionResponse(e,s),o.info("Finished processing queue."),n._processingQueue=!1},e)}catch(u){o.error("Error processing queue: "+u),this.suspendProcessing(),this._processingQueue=!1}}},e.prototype.suspendProcessing=function(e,t,n){var r=this._config;(!e||0>=e)&&(e=5),r.log.info("Suspending processing for "+e+" minutes."),this._suspendProcessingUntil=new Date((new Date).getTime()+6e4*e),t&&(this._discardQueuedItemsUntil=new Date((new Date).getTime()+6e4*e)),n&&this.removeEvents(r.storage.getList("ex-q"))},e.prototype.areQueuedItemsDiscarded=function(){return this._discardQueuedItemsUntil&&this._discardQueuedItemsUntil>new Date},e.prototype.ensureQueueTimer=function(){var e=this;this._queueTimer||(this._queueTimer=setInterval(function(){return e.onProcessQueue()},1e4))},e.prototype.isQueueProcessingSuspended=function(){return this._suspendProcessingUntil&&this._suspendProcessingUntil>new Date},e.prototype.onProcessQueue=function(){this.isQueueProcessingSuspended()||this._processingQueue||this.process()},e.prototype.processSubmissionResponse=function(e,t){var n="The event will not be submitted.",r=this._config,i=r.log;if(e.success)return i.info("Sent "+t.length+" events."),void this.removeEvents(t);if(e.serviceUnavailable)return i.error("Server returned service unavailable."),void this.suspendProcessing();if(e.paymentRequired)return i.info("Too many events have been submitted, please upgrade your plan."),void this.suspendProcessing(null,!0,!0);if(e.unableToAuthenticate)return i.info("Unable to authenticate, please check your configuration. "+n),this.suspendProcessing(15),void this.removeEvents(t);if(e.notFound||e.badRequest)return i.error("Error while trying to submit data: "+e.message),this.suspendProcessing(240),void this.removeEvents(t);if(e.requestEntityTooLarge){var o="Event submission discarded for being too large.";return void(r.submissionBatchSize>1?(i.error(o+" Retrying with smaller batch size."),r.submissionBatchSize=Math.max(1,Math.round(r.submissionBatchSize/1.5))):(i.error(o+" "+n),this.removeEvents(t)))}e.success||(i.error("Error submitting events: "+(e.message||"Please check the network tab for more info.")),this.suspendProcessing())},e.prototype.removeEvents=function(e){for(var t=0;t<(e||[]).length;t++)this._config.storage.remove(e[t].path)},e}();t.DefaultEventQueue=p;var g=function(){function e(e){this._items=[],this._maxItems=e>0?e:250}return e.prototype.save=function(e,t){return e&&t?(this.remove(e),this._items.push({created:(new Date).getTime(),path:e,value:t})>this._maxItems&&this._items.shift(),!0):!1},e.prototype.get=function(e){var t=e?this.getList("^"+e+"$",1)[0]:null;return t?t.value:null},e.prototype.getList=function(e,t){var n=this._items;if(!e)return n.slice(0,t);for(var r=new RegExp(e),i=[],o=0;o<n.length&&!(r.test(n[o].path)&&(i.push(n[o]),i.length>=t));o++);return i},e.prototype.remove=function(e){if(e){var t=this.getList("^"+e+"$",1)[0];t&&this._items.splice(this._items.indexOf(t),1)}},e}();t.InMemoryStorage=g;var d=function(){function e(){this.configurationVersionHeader="x-exceptionless-configversion"}return e.prototype.postEvents=function(e,t,n,r){var i=h.stringify(e,t.dataExclusions),o=this.createRequest(t,"POST","/api/v2/events",i),s=this.createSubmissionCallback(t,n);return t.submissionAdapter.sendRequest(o,s,r)},e.prototype.postUserDescription=function(e,t,n,r){var i="/api/v2/events/by-ref/"+encodeURIComponent(e)+"/user-description",o=h.stringify(t,n.dataExclusions),s=this.createRequest(n,"POST",i,o),u=this.createSubmissionCallback(n,r);return n.submissionAdapter.sendRequest(s,u)},e.prototype.getSettings=function(e,t){var n=this.createRequest(e,"GET","/api/v2/projects/config"),r=function(n,r,i,o){if(200!==n)return t(new I(!1,null,-1,null,r));var s;try{s=JSON.parse(i)}catch(u){e.log.error("Unable to parse settings: '"+i+"'")}return!s||isNaN(s.version)?t(new I(!1,null,-1,null,"Invalid configuration settings.")):void t(new I(!0,s.settings||{},s.version))};return e.submissionAdapter.sendRequest(n,r)},e.prototype.createRequest=function(e,t,n,r){return void 0===r&&(r=null),{method:t,path:n,data:r,serverUrl:e.serverUrl,apiKey:e.apiKey,userAgent:e.userAgent}},e.prototype.createSubmissionCallback=function(e,t){var n=this;return function(r,i,s,u){var a=u&&parseInt(u[n.configurationVersionHeader],10);o.checkVersion(a,e),t(new b(r,i))}},e}();t.DefaultSubmissionClient=d;var h=function(){function e(){}return e.addRange=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];if(e||(e=[]),!t||0===t.length)return e;for(var r=0;r<t.length;r++)t[r]&&e.indexOf(t[r])<0&&e.push(t[r]);return e},e.getHashCode=function(e){if(!e||0===e.length)return null;for(var t=0,n=0;n<e.length;n++){var r=e.charCodeAt(n);t=(t<<5)-t+r,t|=0}return t.toString()},e.getCookies=function(e){for(var t={},n=(e||"").split("; "),r=0;r<n.length;r++){var i=n[r].split("=");t[i[0]]=i[1]}return t},e.guid=function(){function e(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return e()+e()+"-"+e()+"-"+e()+"-"+e()+"-"+e()+e()+e()},e.merge=function(e,t){var n={};for(var r in e||{})e[r]&&(n[r]=e[r]);for(var r in t||{})t[r]&&(n[r]=t[r]);return n},e.parseVersion=function(e){if(!e)return null;var t=/(v?((\d+)\.(\d+)(\.(\d+))?)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?)/,n=t.exec(e);return n&&n.length>0?n[0]:null},e.parseQueryString=function(e){if(!e||0===e.length)return null;var t=e.split("&");if(0===t.length)return null;for(var n={},r=0;r<t.length;r++){var i=t[r].split("=");n[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return n},e.randomNumber=function(){return Math.floor(9007199254740992*Math.random())},e.stringify=function(e,t){function n(e,t){if(!e||!t||"string"!=typeof t)return!1;var n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;if(e=e.toLowerCase().replace(n,""),t=t.toLowerCase().replace(n,""),e.length<=0)return!1;var r="*"===e[0];r&&(e=e.slice(1));var i="*"===e[e.length-1];return i&&(e=e.substring(0,e.length-1)),r&&i?-1!==t.indexOf(e):r?t.lastIndexOf(e)===t.length-e.length:i?0===t.indexOf(e):t===e}function r(e,t){var r=[];return JSON.stringify(e,function(e,i){for(var o=0;o<(t||[]).length;o++)if(n(t[o],e))return;if("object"==typeof i&&i){if(-1!==r.indexOf(i))return;r.push(i)}return i})}if("[object Array]"==={}.toString.call(e)){for(var i=[],o=0;o<e.length;o++)i[o]=JSON.parse(r(e[o],t||[]));return JSON.stringify(i)}return r(e,t||[])},e}();t.Utils=h;var v=function(){function e(t){function n(e){return"function"==typeof e?e(this):e}this.defaultTags=[],this.defaultData={},this.enabled=!0,this.lastReferenceIdManager=new s,this.settings={},this._plugins=[],this._serverUrl="https://collector.exceptionless.io",this._dataExclusions=[],t=h.merge(e.defaults,t),this.log=n(t.log)||new a,this.apiKey=t.apiKey,this.serverUrl=t.serverUrl,this.environmentInfoCollector=n(t.environmentInfoCollector),this.errorParser=n(t.errorParser),this.lastReferenceIdManager=n(t.lastReferenceIdManager)||new s,this.moduleCollector=n(t.moduleCollector),this.requestInfoCollector=n(t.requestInfoCollector),this.submissionBatchSize=n(t.submissionBatchSize)||50,this.submissionAdapter=n(t.submissionAdapter),this.submissionClient=n(t.submissionClient)||new d,this.storage=n(t.storage)||new g,this.queue=n(t.queue)||new p(this),o.applySavedServerSettings(this),l.addDefaultPlugins(this)}return Object.defineProperty(e.prototype,"apiKey",{get:function(){return this._apiKey},set:function(e){this._apiKey=e||null,this.log.info("apiKey: "+this._apiKey)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isValid",{get:function(){return!!this.apiKey&&this.apiKey.length>=10},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"serverUrl",{get:function(){return this._serverUrl},set:function(e){e&&(this._serverUrl=e,this.log.info("serverUrl: "+this._serverUrl))},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"dataExclusions",{get:function(){var e=this.settings["@@DataExclusions"];return this._dataExclusions.concat(e&&e.split(",")||[])},enumerable:!0,configurable:!0}),e.prototype.addDataExclusions=function(){for(var e=[],t=0;t<arguments.length;t++)e[t-0]=arguments[t];this._dataExclusions=h.addRange.apply(h,[this._dataExclusions].concat(e))},Object.defineProperty(e.prototype,"plugins",{get:function(){return this._plugins.sort(function(e,t){return e.priority<t.priority?-1:e.priority>t.priority?1:0})},enumerable:!0,configurable:!0}),e.prototype.addPlugin=function(e,t,n){var r=n?{name:e,priority:t,run:n}:e;if(!r||!r.run)return void this.log.error("Add plugin failed: Run method not defined");r.name||(r.name=h.guid()),r.priority||(r.priority=0);for(var i=!1,o=this._plugins,s=0;s<o.length;s++)if(o[s].name===r.name){i=!0;break}i||o.push(r)},e.prototype.removePlugin=function(e){var t="string"==typeof e?e:e.name;if(!t)return void this.log.error("Remove plugin failed: Plugin name not defined");for(var n=this._plugins,r=0;r<n.length;r++)if(n[r].name===t){n.splice(r,1);break}},e.prototype.setVersion=function(e){e&&(this.defaultData["@version"]=e)},e.prototype.setUserIdentity=function(e,t){var n="@user",r="string"!=typeof e?e:{identity:e,name:t},i=!r||!r.identity&&!r.name;i?delete this.defaultData[n]:this.defaultData[n]=r,this.log.info("user identity: "+(i?"null":r.identity))},Object.defineProperty(e.prototype,"userAgent",{get:function(){return"exceptionless-js/1.1.1"},enumerable:!0,configurable:!0}),e.prototype.useReferenceIds=function(){this.addPlugin(new f)},e.prototype.useDebugLogger=function(){this.log=new u},Object.defineProperty(e,"defaults",{get:function(){return null===e._defaultSettings&&(e._defaultSettings={}),e._defaultSettings},enumerable:!0,configurable:!0}),e._defaultSettings=null,e}();t.Configuration=v;var m=function(){function e(e,t,n){this._validIdentifierErrorMessage="must contain between 8 and 100 alphanumeric or '-' characters.",this.target=e,this.client=t,this.pluginContextData=n||new y}return e.prototype.setType=function(e){return e&&(this.target.type=e),this},e.prototype.setSource=function(e){return e&&(this.target.source=e),this},e.prototype.setSessionId=function(e){if(!this.isValidIdentifier(e))throw new Error("SessionId "+this._validIdentifierErrorMessage);return this.target.session_id=e,this},e.prototype.setReferenceId=function(e){if(!this.isValidIdentifier(e))throw new Error("ReferenceId "+this._validIdentifierErrorMessage);return this.target.reference_id=e,this},e.prototype.setMessage=function(e){return e&&(this.target.message=e),this},e.prototype.setGeo=function(e,t){if(-90>e||e>90)throw new Error("Must be a valid latitude value between -90.0 and 90.0.");if(-180>t||t>180)throw new Error("Must be a valid longitude value between -180.0 and 180.0.");return this.target.geo=e+","+t,this},e.prototype.setUserIdentity=function(e,t){var n="string"!=typeof e?e:{identity:e,name:t};return n&&(n.identity||n.name)?(this.setProperty("@user",n),this):this},e.prototype.setValue=function(e){return e&&(this.target.value=e),this},e.prototype.addTags=function(){for(var e=[],t=0;t<arguments.length;t++)e[t-0]=arguments[t];return this.target.tags=h.addRange.apply(h,[this.target.tags].concat(e)),this},e.prototype.setProperty=function(e,t){return e&&void 0!==t&&null!=t?(this.target.data||(this.target.data={}),this.target.data[e]=t,this):this},e.prototype.markAsCritical=function(e){return e&&this.addTags("Critical"),this},e.prototype.addRequestInfo=function(e){return e&&(this.pluginContextData["@request"]=e),this},e.prototype.submit=function(e){this.client.submitEvent(this.target,this.pluginContextData,e)},e.prototype.isValidIdentifier=function(e){if(!e)return!0;if(e.length<8||e.length>100)return!1;for(var t=0;t<e.length;t++){var n=e.charCodeAt(t),r=n>=48&&57>=n,i=n>=65&&90>=n||n>=97&&122>=n,o=45===n;if(!r&&!i&&!o)return!1}return!0},e}();t.EventBuilder=m;var y=function(){function e(){}return e.prototype.setException=function(e){e&&(this["@@_Exception"]=e)},Object.defineProperty(e.prototype,"hasException",{get:function(){return!!this["@@_Exception"]},enumerable:!0,configurable:!0}),e.prototype.getException=function(){return this["@@_Exception"]||null},e.prototype.markAsUnhandledError=function(){this["@@_IsUnhandledError"]=!0},Object.defineProperty(e.prototype,"isUnhandledError",{get:function(){return!!this["@@_IsUnhandledError"]},enumerable:!0,configurable:!0}),e.prototype.setSubmissionMethod=function(e){e&&(this["@@_SubmissionMethod"]=e)},e.prototype.getSubmissionMethod=function(){return this["@@_SubmissionMethod"]||null},e}();t.ContextData=y;var b=function(){function e(e,t){this.success=!1,this.badRequest=!1,this.serviceUnavailable=!1,this.paymentRequired=!1,this.unableToAuthenticate=!1,this.notFound=!1,this.requestEntityTooLarge=!1,this.statusCode=e,this.message=t,this.success=e>=200&&299>=e,this.badRequest=400===e,this.serviceUnavailable=503===e,this.paymentRequired=402===e,this.unableToAuthenticate=401===e||403===e,this.notFound=404===e,this.requestEntityTooLarge=413===e}return e}();t.SubmissionResponse=b;var x=function(){function e(e,t){"object"!=typeof e?this.config=new v(e):this.config=new v({apiKey:e,serverUrl:t})}return e.prototype.createException=function(e){var t=new y;return t.setException(e),this.createEvent(t).setType("error")},e.prototype.submitException=function(e,t){this.createException(e).submit(t)},e.prototype.createUnhandledException=function(e,t){var n=this.createException(e);return n.pluginContextData.markAsUnhandledError(),n.pluginContextData.setSubmissionMethod(t),n},e.prototype.submitUnhandledException=function(e,t,n){this.createUnhandledException(e,t).submit(n)},e.prototype.createFeatureUsage=function(e){return this.createEvent().setType("usage").setSource(e)},e.prototype.submitFeatureUsage=function(e,t){this.createFeatureUsage(e).submit(t)},e.prototype.createLog=function(e,t,n){var r=this.createEvent().setType("log");if(t&&n)r=r.setSource(e).setMessage(t).setProperty("@level",n);else if(t)r=r.setSource(e).setMessage(t);else{var i=arguments.callee.caller;r=r.setSource(i&&i.name).setMessage(e)}return r},e.prototype.submitLog=function(e,t,n,r){this.createLog(e,t,n).submit(r)},e.prototype.createNotFound=function(e){return this.createEvent().setType("404").setSource(e)},e.prototype.submitNotFound=function(e,t){this.createNotFound(e).submit(t)},e.prototype.createSessionStart=function(e){return this.createEvent().setType("start").setSessionId(e)},e.prototype.submitSessionStart=function(e,t){this.createSessionStart(e).submit(t)},e.prototype.createSessionEnd=function(e){return this.createEvent().setType("end").setSessionId(e)},e.prototype.submitSessionEnd=function(e,t){this.createSessionEnd(e).submit(t)},e.prototype.createEvent=function(e){return new m({date:new Date},this,e)},e.prototype.submitEvent=function(e,t,n){function r(e){return e&&(e.cancelled=!0),!!n&&n(e)}var i=new c(this,e,t);return e?this.config.enabled?(e.data||(e.data={}),e.tags&&e.tags.length||(e.tags=[]),void l.run(i,function(e){var t=e.event;if(!e.cancelled){t.type&&0!==t.type.length||(t.type="log"),t.date||(t.date=new Date);var r=e.client.config;r.queue.enqueue(t),t.reference_id&&t.reference_id.length>0&&(e.log.info("Setting last reference id '"+t.reference_id+"'"),r.lastReferenceIdManager.setLast(t.reference_id))}!!n&&n(e)})):(this.config.log.info("Event submission is currently disabled."),r(i)):r(i)},e.prototype.updateUserEmailAndDescription=function(e,t,n,r){var i=this;if(!(e&&t&&n&&this.config.enabled))return!!r&&r(new b(500,"cancelled"));var o={email_address:t,description:n};this.config.submissionClient.postUserDescription(e,o,this.config,function(t){t.success||i.config.log.error("Failed to submit user email and description for event '"+e+"': "+t.statusCode+" "+t.message),!!r&&r(t)})},e.prototype.getLastReferenceId=function(){return this.config.lastReferenceIdManager.getLast()},Object.defineProperty(e,"default",{get:function(){return null===e._instance&&(e._instance=new e(null)),e._instance},enumerable:!0,configurable:!0}),e._instance=null,e}();t.ExceptionlessClient=x;var S=function(){function e(){this.priority=10,this.name="ConfigurationDefaultsPlugin"}return e.prototype.run=function(e,t){for(var n=e.client.config.defaultTags||[],r=0;r<n.length;r++){var i=n[r];i&&e.event.tags.indexOf(i)<0&&e.event.tags.push(i)}var o=e.client.config.defaultData||{};for(var s in o)o[s]&&(e.event.data[s]=o[s]);t&&t()},e}();t.ConfigurationDefaultsPlugin=S;var E=function(){function e(){this.priority=30,this.name="ErrorPlugin",this.ignoredProperties=["arguments","column","columnNumber","description","fileName","message","name","number","line","lineNumber","opera#sourceloc","sourceURL","stack","stacktrace"]}return e.prototype.run=function(e,t){var n="@error",r="@ext",i=e.contextData.getException();if(i&&(e.event.type="error",!e.event.data[n])){var o=e.client.config.errorParser;if(!o)throw new Error("No error parser was defined.");var s=o.parse(e,i);if(s){var u=this.getAdditionalData(i);u&&(s.data||(s.data={}),s.data[r]=u),e.event.data[n]=s}}t&&t()},e.prototype.getAdditionalData=function(e){var t=this,n=Object.keys(e).filter(function(e){return t.ignoredProperties.indexOf(e)<0});if(0===n.length)return null;var r={};return n.forEach(function(t){var n=e[t];"function"!=typeof n&&(r[t]=n)}),r},e}();t.ErrorPlugin=E;var w=function(){function e(){this.priority=40,this.name="ModuleInfoPlugin"}return e.prototype.run=function(e,t){var n="@error",r=e.client.config.moduleCollector;if(e.event.data[n]&&!e.event.data["@error"].modules&&r){var i=r.getModules(e);i&&i.length>0&&(e.event.data[n].modules=i)}t&&t()},e}();t.ModuleInfoPlugin=w;var _=function(){function e(){this.priority=60,this.name="RequestInfoPlugin"}return e.prototype.run=function(e,t){var n="@request",r=e.client.config.requestInfoCollector;if(!e.event.data[n]&&r){var i=r.getRequestInfo(e);i&&(e.event.data[n]=i)}t&&t()},e}();t.RequestInfoPlugin=_;var T=function(){function e(){this.priority=70,this.name="EnvironmentInfoPlugin"}return e.prototype.run=function(e,t){var n="@environment",r=e.client.config.environmentInfoCollector;if(!e.event.data[n]&&r){var i=r.getEnvironmentInfo(e);i&&(e.event.data[n]=i)}t&&t()},e}();t.EnvironmentInfoPlugin=T;var P=function(){function e(){this.priority=100,this.name="SubmissionMethodPlugin"}return e.prototype.run=function(e,t){var n=e.contextData.getSubmissionMethod();n&&(e.event.data["@submission_method"]=n),t&&t()},e}();t.SubmissionMethodPlugin=P;var I=function(){function e(e,t,n,r,i){void 0===n&&(n=-1),void 0===r&&(r=null),void 0===i&&(i=null),this.success=!1,this.settingsVersion=-1,this.success=e,this.settings=t,this.settingsVersion=n,this.exception=r,this.message=i}return e;
+}();t.SettingsResponse=I;var C=function(){function e(){}return e.prototype.parse=function(e,t){function n(e){for(var t=("string"==typeof e?[e]:e)||[],n=[],r=0;r<t.length;r++)n.push({name:t[r]});return n}function r(e){for(var t="<anonymous>",r=[],i=0;i<e.length;i++){var o=e[i];r.push({name:(o.func||t).replace("?",t),parameters:n(o.args),file_name:o.url,line_number:o.line||0,column:o.column||0})}return r}var i="@@_TraceKit.StackTrace",o=e.contextData[i]?e.contextData[i]:TraceKit.computeStackTrace(t,25);if(!o)throw new Error("Unable to parse the exceptions stack trace.");return{type:o.name,message:o.message||t.message,stack_trace:r(o.stack||[])}},e}();t.DefaultErrorParser=C;var R=function(){function e(){}return e.prototype.getModules=function(e){if(document&&document.getElementsByTagName)return null;var t=[],n=document.getElementsByTagName("script");if(n&&n.length>0)for(var r=0;r<n.length;r++)n[r].src?t.push({module_id:r,name:n[r].src,version:h.parseVersion(n[r].src)}):n[r].innerHTML&&t.push({module_id:r,name:"Script Tag",version:h.getHashCode(n[r].innerHTML)});return t},e}();t.DefaultModuleCollector=R;var k=function(){function e(){}return e.prototype.getRequestInfo=function(e){if(!document||!navigator||!location)return null;var t={user_agent:navigator.userAgent,is_secure:"https:"===location.protocol,host:location.hostname,port:location.port&&""!==location.port?parseInt(location.port,10):80,path:location.pathname,cookies:h.getCookies(document.cookie),query_string:h.parseQueryString(location.search.substring(1))};return document.referrer&&""!==document.referrer&&(t.referrer=document.referrer),t},e}();t.DefaultRequestInfoCollector=k;var U=function(){function e(){}return e.prototype.sendRequest=function(e,t,n){function r(n,r){function i(e){function t(e){return e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}for(var n={},r=(e||"").split("\r\n"),i=0;i<r.length;i++){var o=r[i],s=o.indexOf(": ");s>0&&(n[t(o.substring(0,s).toLowerCase())]=o.substring(s+2))}return n}if(!a){a=!0;var u=r.statusText,c=r.responseText,l=r.status;if(n===o||0===l)u="Unable to connect to server.",l=0;else if(n!==s||l){if(200>l||l>299){var f=r.responseBody;if(f&&f.message)u=f.message;else if(c&&-1!==c.indexOf("message"))try{u=JSON.parse(c).message}catch(p){u=c}}}else l="POST"===e.method?202:200;t(l||500,u||"",c,i(r.getAllResponseHeaders&&r.getAllResponseHeaders()))}}function i(e,t,n){var r=new XMLHttpRequest;return u in r?(r.open(t,n,!0),r.setRequestHeader("X-Exceptionless-Client",e),"POST"===t&&r.setRequestHeader("Content-Type","application/json")):"undefined"!=typeof XDomainRequest?(c=!0,r=new XDomainRequest,r.open(t,"http:"===location.protocol?n.replace("https:","http:"):n)):r=null,r&&(r.timeout=1e4),r}var o="timeout",s="loaded",u="withCredentials",a=!1,c=!1,l=""+e.serverUrl+e.path+"?access_token="+encodeURIComponent(e.apiKey),f=i(e.userAgent,e.method||"POST",l);return f?(u in f&&(f.onreadystatechange=function(){4===f.readyState&&r(s,f)}),f.onprogress=function(){},f.ontimeout=function(){return r(o,f)},f.onerror=function(){return r("error",f)},f.onload=function(){return r(s,f)},void(c?setTimeout(function(){return f.send(e.data)},500):f.send(e.data))):t(503,"CORS not supported.")},e}();t.DefaultSubmissionAdapter=U;var q=v.defaults,D=r();return D&&(D.apiKey||D.serverUrl)&&(q.apiKey=D.apiKey,q.serverUrl=D.serverUrl),q.errorParser=new C,q.moduleCollector=new R,q.requestInfoCollector=new k,q.submissionAdapter=new U,TraceKit.report.subscribe(i),TraceKit.extendToAsynchronousCallbacks(),Error.stackTraceLimit=1/0,t});
 //# sourceMappingURL=exceptionless.min.js.map
diff --git a/dist/exceptionless.min.js.map b/dist/exceptionless.min.js.map
index 2d8e94e9..4e6e11ef 100644
--- a/dist/exceptionless.min.js.map
+++ b/dist/exceptionless.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["tracekit.js","/source/exceptionless.ts","exceptionless.min.js"],"names":["window","undefined","_has","object","key","Object","prototype","hasOwnProperty","call","_isUndefined","what","TraceKit","_oldTraceKit","_slice","slice","UNKNOWN_FUNCTION","noConflict","wrap","func","wrapped","apply","this","arguments","e","report","subscribe","handler","installGlobalHandler","handlers","push","unsubscribe","i","length","splice","notifyHandlers","stack","isWindowError","exception","collectWindowErrors","concat","inner","traceKitWindowOnError","message","url","lineNo","columnNo","errorObj","computeStackTrace","lastExceptionStack","augmentStackTraceWithInitialElement","lastException","location","line","column","guessFunctionName","context","gatherContext","mode","_oldOnerrorHandler","_onErrorHandlerInstalled","onerror","ex","args","s","setTimeout","incomplete","loadSource","remoteFetching","getXHR","XMLHttpRequest","ActiveXObject","request","open","send","responseText","getSource","sourceCache","source","domain","document","indexOf","split","m","reFunctionArgNames","reGuessFunction","maxLines","exec","linesBefore","Math","floor","linesOfContext","linesAfter","start","max","end","min","escapeRegExp","text","replace","escapeCodeAsRegExpForMatchingInsideHTML","body","findSourceInUrls","re","urls","j","join","substring","index","lastIndexOf","findSourceInLine","fragment","RegExp","findSourceByFunctionBody","parts","result","href","scripts","getElementsByTagName","code","codeRE","eventRE","script","src","name","event","computeStackTraceFromStackProp","element","chrome","gecko","winjs","lines","reference","isNative","columnNumber","computeStackTraceFromStacktraceProp","stacktrace","opera10Regex","opera11Regex","exc","computeStackTraceFromOperaMultiLineMessage","lineRE1","lineRE2","lineRE3","inlineScriptBlocks","item","relativeLine","pos","innerText","midline","stackInfo","initial","unshift","partial","computeStackTraceByWalkingCallerChain","depth","functionName","funcs","recursion","curr","caller","toString","input","description","sourceURL","fileName","lineNumber","debug","computeStackTraceOfCaller","Error","ofCaller","extendToAsynchronousCallbacks","_helper","fnName","originalFn","originalCallback","global","root","factory","define","amd","exports","module","require","exceptionless","getDefaultsSettingsFromScriptTag","Utils","parseQueryString","pop","processUnhandledException","stackTrace","options","builder","ExceptionlessClient","createUnhandledException","status","pluginContextData","submit","SettingsManager","_handlers","changed","config","checkVersion","version","savedConfigVersion","parseInt","storage","get","_configPath","isNaN","log","info","updateSettings","_this","isValid","submissionClient","getSettings","response","success","settings","merge","savedServerSettings","getSavedServerSettings","path","save","settingsVersion","error","DefaultLastReferenceIdManager","_lastReferenceId","getLast","clearLast","setLast","eventId","ConsoleLog","warn","level","console","NullLog","EventPluginContext","client","contextData","ContextData","defineProperty","enumerable","configurable","EventPluginManager","run","callback","plugin","next","cancelled","plugins","wrappedPlugins","priority","addDefaultPlugins","addPlugin","ConfigurationDefaultsPlugin","ErrorPlugin","ModuleInfoPlugin","RequestInfoPlugin","EnvironmentInfoPlugin","SubmissionMethodPlugin","ReferenceIdPlugin","reference_id","type","guid","DefaultEventQueue","_processingQueue","_config","enqueue","ensureQueueTimer","areQueuedItemsDiscarded","Date","toJSON","randomNumber","process","isAppExiting","getEvents","events","items","value","queueNotProcessed","enabled","getList","submissionBatchSize","serverUrl","postEvents","processSubmissionResponse","suspendProcessing","durationInMinutes","discardFutureQueuedItems","clearQueue","_suspendProcessingUntil","getTime","_discardQueuedItemsUntil","removeEvents","_queueTimer","setInterval","onProcessQueue","isQueueProcessingSuspended","noSubmission","serviceUnavailable","paymentRequired","unableToAuthenticate","notFound","badRequest","requestEntityTooLarge","round","remove","InMemoryStorage","maxItems","_items","_maxItems","created","shift","searchPattern","limit","regex","results","test","DefaultSubmissionClient","configurationVersionHeader","data","stringify","dataExclusions","createRequest","cb","createSubmissionCallback","submissionAdapter","sendRequest","postUserDescription","referenceId","encodeURIComponent","headers","SettingsResponse","JSON","parse","method","apiKey","userAgent","SubmissionResponse","addRange","target","values","_i","getHashCode","hash","character","charCodeAt","getCookies","cookies","cookie","s4","random","defaultValues","parseVersion","versionRegex","matches","query","pairs","pair","decodeURIComponent","exclusions","checkForMatch","pattern","trim","toLowerCase","startsWithWildcard","endsWithWildcard","stringifyImpl","obj","excludedKeys","cache","Configuration","configSettings","inject","fn","defaultTags","defaultData","lastReferenceIdManager","_plugins","_serverUrl","_dataExclusions","defaults","environmentInfoCollector","errorParser","moduleCollector","requestInfoCollector","queue","applySavedServerSettings","_apiKey","set","addDataExclusions","sort","p1","p2","pluginOrName","pluginAction","pluginExists","removePlugin","setVersion","setUserIdentity","userInfoOrIdentity","USER_KEY","userInfo","identity","shouldRemove","useReferenceIds","useDebugLogger","_defaultSettings","EventBuilder","_validIdentifierErrorMessage","setType","setSource","setSessionId","sessionId","isValidIdentifier","session_id","setReferenceId","setMessage","setGeo","latitude","longitude","geo","setProperty","setValue","addTags","tags","markAsCritical","critical","addRequestInfo","submitEvent","isDigit","isLetter","isMinus","setException","getException","markAsUnhandledError","setSubmissionMethod","getSubmissionMethod","statusCode","settingsOrApiKey","createException","createEvent","submitException","submissionMethod","submitUnhandledException","createFeatureUsage","feature","submitFeatureUsage","createLog","sourceOrMessage","callee","submitLog","createNotFound","resource","submitNotFound","createSessionStart","submitSessionStart","createSessionEnd","submitSessionEnd","date","ctx","ev","updateUserEmailAndDescription","email","userDescription","email_address","getLastReferenceId","_instance","tag","ERROR_KEY","parser","collector","modules","getModules","REQUEST_KEY","requestInfo","getRequestInfo","ENVIRONMENT_KEY","environmentInfo","getEnvironmentInfo","DefaultErrorParser","getParameters","parameters","params","getStackFrames","stackFrames","ANONYMOUS","frames","frame","file_name","line_number","TRACEKIT_STACK_TRACE_KEY","stack_trace","DefaultModuleCollector","module_id","innerHTML","DefaultRequestInfoCollector","navigator","user_agent","is_secure","protocol","host","hostname","port","pathname","query_string","search","referrer","DefaultSubmissionAdapter","complete","xhr","parseResponseHeaders","headerStr","headerPairs","headerPair","separator","isCompleted","statusText","TIMEOUT","LOADED","responseBody","getAllResponseHeaders","WITH_CREDENTIALS","setRequestHeader","XDomainRequest","useSetTimeout","timeout","onreadystatechange","readyState","onprogress","ontimeout","onload","stackTraceLimit","Infinity"],"mappings":"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","file":"exceptionless.min.js","sourcesContent":["/*\n TraceKit - Cross browser stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n    return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n    return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n    return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n    window.TraceKit = _oldTraceKit;\n    return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n    function wrapped() {\n        try {\n            return func.apply(this, arguments);\n        } catch (e) {\n            TraceKit.report(e);\n            throw e;\n        }\n    }\n    return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n *   TraceKit.report.subscribe(function(stackInfo) { ... })\n *   TraceKit.report.unsubscribe(function(stackInfo) { ... })\n *   TraceKit.report(exception)\n *   try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n *   - Firefox: full stack trace with line numbers, plus column number\n *              on top frame; column number is not guaranteed\n *   - Opera:   full stack trace with line and column numbers\n *   - Chrome:  full stack trace with line and column numbers\n *   - Safari:  line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *   - IE:      line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n *   - IE5.5+ (only 8.0 tested)\n *   - Firefox 0.9+ (only 3.5+ tested)\n *   - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n *     Exceptions Have Stacktrace to be enabled in opera:config)\n *   - Safari 3+ (only 4+ tested)\n *   - Chrome 1+ (only 5+ tested)\n *   - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n    var handlers = [],\n        lastException = null,\n        lastExceptionStack = null;\n\n    /**\n     * Add a crash handler.\n     * @param {Function} handler\n     */\n    function subscribe(handler) {\n        installGlobalHandler();\n        handlers.push(handler);\n    }\n\n    /**\n     * Remove a crash handler.\n     * @param {Function} handler\n     */\n    function unsubscribe(handler) {\n        for (var i = handlers.length - 1; i >= 0; --i) {\n            if (handlers[i] === handler) {\n                handlers.splice(i, 1);\n            }\n        }\n    }\n\n    /**\n     * Dispatch stack information to all handlers.\n     * @param {Object.<string, *>} stack\n     */\n    function notifyHandlers(stack, isWindowError) {\n        var exception = null;\n        if (isWindowError && !TraceKit.collectWindowErrors) {\n          return;\n        }\n        for (var i in handlers) {\n            if (_has(handlers, i)) {\n                try {\n                    handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n                } catch (inner) {\n                    exception = inner;\n                }\n            }\n        }\n\n        if (exception) {\n            throw exception;\n        }\n    }\n\n    var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n    /**\n     * Ensures all global unhandled exceptions are recorded.\n     * Supported by Gecko and IE.\n     * @param {string} message Error message.\n     * @param {string} url URL of script that generated the exception.\n     * @param {(number|string)} lineNo The line number at which the error\n     * occurred.\n     * @param {?(number|string)} columnNo The column number at which the error\n     * occurred.\n     * @param {?Error} errorObj The actual Error object.\n     */\n    function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n        var stack = null;\n\n        if (errorObj) {\n          stack = TraceKit.computeStackTrace(errorObj);\n        }\n        else\n        {\n            if (lastExceptionStack) {\n                TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n                stack = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n            } else {\n                var location = {\n                    'url': url,\n                    'line': lineNo,\n                    'column': columnNo\n                };\n                location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n                location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n                stack = {\n                    'mode': 'onerror',\n                    'message': message,\n                    'stack': [location]\n                };\n            }\n        }\n\n        notifyHandlers(stack, 'from window.onerror');\n\n        if (_oldOnerrorHandler) {\n            return _oldOnerrorHandler.apply(this, arguments);\n        }\n\n        return false;\n    }\n\n    function installGlobalHandler ()\n    {\n        if (_onErrorHandlerInstalled === true) {\n            return;\n        }\n        _oldOnerrorHandler = window.onerror;\n        window.onerror = traceKitWindowOnError;\n        _onErrorHandlerInstalled = true;\n    }\n\n    /**\n     * Reports an unhandled Error to TraceKit.\n     * @param {Error} ex\n     */\n    function report(ex) {\n        var args = _slice.call(arguments, 1);\n        if (lastExceptionStack) {\n            if (lastException === ex) {\n                return; // already caught by an inner catch block, ignore\n            } else {\n                var s = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [s, null].concat(args));\n            }\n        }\n\n        var stack = TraceKit.computeStackTrace(ex);\n        lastExceptionStack = stack;\n        lastException = ex;\n\n        // If the stack trace is incomplete, wait for 2 seconds for\n        // slow slow IE to see if onerror occurs or not before reporting\n        // this exception; otherwise, we will end up with an incomplete\n        // stack trace\n        window.setTimeout(function () {\n            if (lastException === ex) {\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [stack, null].concat(args));\n            }\n        }, (stack.incomplete ? 2000 : 0));\n\n        throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n    }\n\n    report.subscribe = subscribe;\n    report.unsubscribe = unsubscribe;\n    return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n *   s = TraceKit.computeStackTrace.ofCaller([depth])\n *   s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n *   s.name              - exception name\n *   s.message           - exception message\n *   s.stack[i].url      - JavaScript or HTML file URL\n *   s.stack[i].func     - function name, or empty for anonymous functions (if guessing did not work)\n *   s.stack[i].args     - arguments passed to the function, if known\n *   s.stack[i].line     - line number, if known\n *   s.stack[i].column   - column number, if known\n *   s.stack[i].context  - an array of source code lines; the middle element corresponds to the correct line#\n *   s.mode              - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n *   - Firefox:  full stack trace with line numbers and unreliable column\n *               number on top frame\n *   - Opera 10: full stack trace with line and column numbers\n *   - Opera 9-: full stack trace with line numbers\n *   - Chrome:   full stack trace with line and column numbers\n *   - Safari:   line and column number for the topmost stacktrace element\n *               only\n *   - IE:       no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n *  * in Safari, some methods may be missing from the stack trace;\n *  * in IE, the topmost function in the stack trace will always be the\n *    caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n *     function trace(message) {\n *         var stackInfo = TraceKit.computeStackTrace.ofCaller();\n *         var data = message + \"\\n\";\n *         for(var i in stackInfo.stack) {\n *             var item = stackInfo.stack[i];\n *             data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n *         }\n *         if (window.console)\n *             console.info(data);\n *         else\n *             alert(data);\n *     }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n    var debug = false,\n        sourceCache = {};\n\n    /**\n     * Attempts to retrieve source code via XMLHttpRequest, which is used\n     * to look up anonymous function names.\n     * @param {string} url URL of source code.\n     * @return {string} Source contents.\n     */\n    function loadSource(url) {\n        if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n            return '';\n        }\n        try {\n            var getXHR = function() {\n                try {\n                    return new window.XMLHttpRequest();\n                } catch (e) {\n                    // explicitly bubble up the exception if not found\n                    return new window.ActiveXObject('Microsoft.XMLHTTP');\n                }\n            };\n\n            var request = getXHR();\n            request.open('GET', url, false);\n            request.send('');\n            return request.responseText;\n        } catch (e) {\n            return '';\n        }\n    }\n\n    /**\n     * Retrieves source code from the source code cache.\n     * @param {string} url URL of source code.\n     * @return {Array.<string>} Source contents.\n     */\n    function getSource(url) {\n        if (typeof url !== 'string') {\n            return [];\n        }\n\n        if (!_has(sourceCache, url)) {\n            // URL needs to be able to fetched within the acceptable domain.  Otherwise,\n            // cross-domain errors will be triggered.\n            var source = '';\n\n            var domain = '';\n            try { domain = document.domain; } catch (e) {}\n            if (url.indexOf(domain) !== -1) {\n                source = loadSource(url);\n            }\n            sourceCache[url] = source ? source.split('\\n') : [];\n        }\n\n        return sourceCache[url];\n    }\n\n    /**\n     * Tries to use an externally loaded copy of source code to determine\n     * the name of a function by looking at the name of the variable it was\n     * assigned to, if any.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} lineNo Line number in source code.\n     * @return {string} The function name, if discoverable.\n     */\n    function guessFunctionName(url, lineNo) {\n        var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n            reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n            line = '',\n            maxLines = 10,\n            source = getSource(url),\n            m;\n\n        if (!source.length) {\n            return UNKNOWN_FUNCTION;\n        }\n\n        // Walk backwards from the first line in the function until we find the line which\n        // matches the pattern above, which is the function definition\n        for (var i = 0; i < maxLines; ++i) {\n            line = source[lineNo - i] + line;\n\n            if (!_isUndefined(line)) {\n                if ((m = reGuessFunction.exec(line))) {\n                    return m[1];\n                } else if ((m = reFunctionArgNames.exec(line))) {\n                    return m[1];\n                }\n            }\n        }\n\n        return UNKNOWN_FUNCTION;\n    }\n\n    /**\n     * Retrieves the surrounding lines from where an exception occurred.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} line Line number in source code to centre\n     * around for context.\n     * @return {?Array.<string>} Lines of source code.\n     */\n    function gatherContext(url, line) {\n        var source = getSource(url);\n\n        if (!source.length) {\n            return null;\n        }\n\n        var context = [],\n            // linesBefore & linesAfter are inclusive with the offending line.\n            // if linesOfContext is even, there will be one extra line\n            //   *before* the offending line.\n            linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n            // Add one extra line if linesOfContext is odd\n            linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n            start = Math.max(0, line - linesBefore - 1),\n            end = Math.min(source.length, line + linesAfter - 1);\n\n        line -= 1; // convert to 0-based index\n\n        for (var i = start; i < end; ++i) {\n            if (!_isUndefined(source[i])) {\n                context.push(source[i]);\n            }\n        }\n\n        return context.length > 0 ? context : null;\n    }\n\n    /**\n     * Escapes special characters, except for whitespace, in a string to be\n     * used inside a regular expression as a string literal.\n     * @param {string} text The string.\n     * @return {string} The escaped string literal.\n     */\n    function escapeRegExp(text) {\n        return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n    }\n\n    /**\n     * Escapes special characters in a string to be used inside a regular\n     * expression as a string literal. Also ensures that HTML entities will\n     * be matched the same as their literal friends.\n     * @param {string} body The string.\n     * @return {string} The escaped string.\n     */\n    function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n        return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('\"', '(?:\"|&quot;)').replace(/\\s+/g, '\\\\s+');\n    }\n\n    /**\n     * Determines where a code fragment occurs in the source code.\n     * @param {RegExp} re The function definition.\n     * @param {Array.<string>} urls A list of URLs to search.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceInUrls(re, urls) {\n        var source, m;\n        for (var i = 0, j = urls.length; i < j; ++i) {\n            // console.log('searching', urls[i]);\n            if ((source = getSource(urls[i])).length) {\n                source = source.join('\\n');\n                if ((m = re.exec(source))) {\n                    // console.log('Found function in ' + urls[i]);\n\n                    return {\n                        'url': urls[i],\n                        'line': source.substring(0, m.index).split('\\n').length,\n                        'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n                    };\n                }\n            }\n        }\n\n        // console.log('no match');\n\n        return null;\n    }\n\n    /**\n     * Determines at which column a code fragment occurs on a line of the\n     * source code.\n     * @param {string} fragment The code fragment.\n     * @param {string} url The URL to search.\n     * @param {(string|number)} line The line number to examine.\n     * @return {?number} The column number.\n     */\n    function findSourceInLine(fragment, url, line) {\n        var source = getSource(url),\n            re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n            m;\n\n        line -= 1;\n\n        if (source && source.length > line && (m = re.exec(source[line]))) {\n            return m.index;\n        }\n\n        return null;\n    }\n\n    /**\n     * Determines where a function was defined within the source code.\n     * @param {(Function|string)} func A function reference or serialized\n     * function definition.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceByFunctionBody(func) {\n        var urls = [window.location.href],\n            scripts = document.getElementsByTagName('script'),\n            body,\n            code = '' + func,\n            codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            re,\n            parts,\n            result;\n\n        for (var i = 0; i < scripts.length; ++i) {\n            var script = scripts[i];\n            if (script.src) {\n                urls.push(script.src);\n            }\n        }\n\n        if (!(parts = codeRE.exec(code))) {\n            re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n        }\n\n        // not sure if this is really necessary, but I don’t have a test\n        // corpus large enough to confirm that and it was in the original.\n        else {\n            var name = parts[1] ? '\\\\s+' + parts[1] : '',\n                args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n            body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n            re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n        }\n\n        // look for a normal function definition\n        if ((result = findSourceInUrls(re, urls))) {\n            return result;\n        }\n\n        // look for an old-school event handler function\n        if ((parts = eventRE.exec(code))) {\n            var event = parts[1];\n            body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n            // look for a function defined in HTML as an onXXX handler\n            re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n            if ((result = findSourceInUrls(re, urls[0]))) {\n                return result;\n            }\n\n            // look for ???\n            re = new RegExp(body);\n\n            if ((result = findSourceInUrls(re, urls))) {\n                return result;\n            }\n        }\n\n        return null;\n    }\n\n    // Contents of Exception in various browsers.\n    //\n    // SAFARI:\n    // ex.message = Can't find variable: qq\n    // ex.line = 59\n    // ex.sourceId = 580238192\n    // ex.sourceURL = http://...\n    // ex.expressionBeginOffset = 96\n    // ex.expressionCaretOffset = 98\n    // ex.expressionEndOffset = 98\n    // ex.name = ReferenceError\n    //\n    // FIREFOX:\n    // ex.message = qq is not defined\n    // ex.fileName = http://...\n    // ex.lineNumber = 59\n    // ex.columnNumber = 69\n    // ex.stack = ...stack trace... (see the example below)\n    // ex.name = ReferenceError\n    //\n    // CHROME:\n    // ex.message = qq is not defined\n    // ex.name = ReferenceError\n    // ex.type = not_defined\n    // ex.arguments = ['aa']\n    // ex.stack = ...stack trace...\n    //\n    // INTERNET EXPLORER:\n    // ex.message = ...\n    // ex.name = ReferenceError\n    //\n    // OPERA:\n    // ex.message = ...message... (see the example below)\n    // ex.name = ReferenceError\n    // ex.opera#sourceloc = 11  (pretty much useless, duplicates the info in ex.message)\n    // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n    /**\n     * Computes stack trace information from the stack property.\n     * Chrome and Gecko use this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStackProp(ex) {\n        if (!ex.stack) {\n            return null;\n        }\n\n        var chrome = /^\\s*at (.*?) ?\\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\\d+))?(?::(\\d+))?\\)?\\s*$/i,\n            gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|chrome|\\[).*?)(?::(\\d+))?(?::(\\d+))?\\s*$/i,\n            winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|http|https):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n            lines = ex.stack.split('\\n'),\n            stack = [],\n            parts,\n            element,\n            reference = /^(.*) is undefined$/.exec(ex.message);\n\n        for (var i = 0, j = lines.length; i < j; ++i) {\n            if ((parts = chrome.exec(lines[i]))) {\n                var isNative = parts[2] && parts[2].indexOf('native') !== -1;\n                element = {\n                    'url': !isNative ? parts[2] : null,\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': isNative ? [parts[2]] : [],\n                    'line': parts[3] ? +parts[3] : null,\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = winjs.exec(lines[i]))) {\n                element = {\n                    'url': parts[2],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': [],\n                    'line': +parts[3],\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = gecko.exec(lines[i]))) {\n                element = {\n                    'url': parts[3],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': parts[2] ? parts[2].split(',') : [],\n                    'line': parts[4] ? +parts[4] : null,\n                    'column': parts[5] ? +parts[5] : null\n                };\n            } else {\n                continue;\n            }\n\n            if (!element.func && element.line) {\n                element.func = guessFunctionName(element.url, element.line);\n            }\n\n            if (element.line) {\n                element.context = gatherContext(element.url, element.line);\n            }\n\n            stack.push(element);\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        if (stack[0] && stack[0].line && !stack[0].column && reference) {\n            stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n        } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n            // FireFox uses this awesome columnNumber property for its top frame\n            // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n            // so adding 1\n            stack[0].column = ex.columnNumber + 1;\n        }\n\n        return {\n            'mode': 'stack',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * Computes stack trace information from the stacktrace property.\n     * Opera 10+ uses this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStacktraceProp(ex) {\n        // Access and store the stacktrace property before doing ANYTHING\n        // else to it because Opera is not very good at providing it\n        // reliably in other circumstances.\n        var stacktrace = ex.stacktrace;\n        if (!stacktrace) {\n            return;\n        }\n\n        var opera10Regex = / line (\\d+).*script (?:in )?(\\S+)(?:: in function (\\S+))?$/i,\n            opera11Regex = / line (\\d+), column (\\d+)\\s*(?:in (?:<anonymous function: ([^>]+)>|([^\\)]+))\\((.*)\\))? in (.*):\\s*$/i,\n            lines = stacktrace.split('\\n'),\n            stack = [],\n            parts;\n\n        for (var line = 0; line < lines.length; line += 2) {\n            var element = null;\n            if ((parts = opera10Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[2],\n                    'line': +parts[1],\n                    'column': null,\n                    'func': parts[3],\n                    'args':[]\n                };\n            } else if ((parts = opera11Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[6],\n                    'line': +parts[1],\n                    'column': +parts[2],\n                    'func': parts[3] || parts[4],\n                    'args': parts[5] ? parts[5].split(',') : []\n                };\n            }\n\n            if (element) {\n                if (!element.func && element.line) {\n                    element.func = guessFunctionName(element.url, element.line);\n                }\n                if (element.line) {\n                    try {\n                        element.context = gatherContext(element.url, element.line);\n                    } catch (exc) {}\n                }\n\n                if (!element.context) {\n                    element.context = [lines[line + 1]];\n                }\n\n                stack.push(element);\n            }\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        return {\n            'mode': 'stacktrace',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * NOT TESTED.\n     * Computes stack trace information from an error message that includes\n     * the stack trace.\n     * Opera 9 and earlier use this method if the option to show stack\n     * traces is turned on in opera:config.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack information.\n     */\n    function computeStackTraceFromOperaMultiLineMessage(ex) {\n        // TODO: Clean this function up\n        // Opera includes a stack trace into the exception message. An example is:\n        //\n        // Statement on line 3: Undefined variable: undefinedFunc\n        // Backtrace:\n        //   Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n        //         undefinedFunc(a);\n        //   Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n        //           zzz(x, y, z);\n        //   Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n        //           yyy(a, a, a);\n        //   Line 1 of function script\n        //     try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n        //   ...\n\n        var lines = ex.message.split('\\n');\n        if (lines.length < 4) {\n            return null;\n        }\n\n        var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n            stack = [],\n            scripts = document.getElementsByTagName('script'),\n            inlineScriptBlocks = [],\n            parts;\n\n        for (var s in scripts) {\n            if (_has(scripts, s) && !scripts[s].src) {\n                inlineScriptBlocks.push(scripts[s]);\n            }\n        }\n\n        for (var line = 2; line < lines.length; line += 2) {\n            var item = null;\n            if ((parts = lineRE1.exec(lines[line]))) {\n                item = {\n                    'url': parts[2],\n                    'func': parts[3],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null\n                };\n            } else if ((parts = lineRE2.exec(lines[line]))) {\n                item = {\n                    'url': parts[3],\n                    'func': parts[4],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.\n                };\n                var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block\n                var script = inlineScriptBlocks[parts[2] - 1];\n                if (script) {\n                    var source = getSource(item.url);\n                    if (source) {\n                        source = source.join('\\n');\n                        var pos = source.indexOf(script.innerText);\n                        if (pos >= 0) {\n                            item.line = relativeLine + source.substring(0, pos).split('\\n').length;\n                        }\n                    }\n                }\n            } else if ((parts = lineRE3.exec(lines[line]))) {\n                var url = window.location.href.replace(/#.*$/, '');\n                var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1]));\n                var src = findSourceInUrls(re, [url]);\n                item = {\n                    'url': url,\n                    'func': '',\n                    'args': [],\n                    'line': src ? src.line : parts[1],\n                    'column': null\n                };\n            }\n\n            if (item) {\n                if (!item.func) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n                var context = gatherContext(item.url, item.line);\n                var midline = (context ? context[Math.floor(context.length / 2)] : null);\n                if (context && midline.replace(/^\\s*/, '') === lines[line + 1].replace(/^\\s*/, '')) {\n                    item.context = context;\n                } else {\n                    // if (context) alert(\"Context mismatch. Correct midline:\\n\" + lines[i+1] + \"\\n\\nMidline:\\n\" + midline + \"\\n\\nContext:\\n\" + context.join(\"\\n\") + \"\\n\\nURL:\\n\" + item.url);\n                    item.context = [lines[line + 1]];\n                }\n                stack.push(item);\n            }\n        }\n        if (!stack.length) {\n            return null; // could not parse multiline exception message as Opera stack trace\n        }\n\n        return {\n            'mode': 'multiline',\n            'name': ex.name,\n            'message': lines[0],\n            'stack': stack\n        };\n    }\n\n    /**\n     * Adds information about the first frame to incomplete stack traces.\n     * Safari and IE require this to get complete data on the first frame.\n     * @param {Object.<string, *>} stackInfo Stack trace information from\n     * one of the compute* methods.\n     * @param {string} url The URL of the script that caused an error.\n     * @param {(number|string)} lineNo The line number of the script that\n     * caused an error.\n     * @param {string=} message The error generated by the browser, which\n     * hopefully contains the name of the object that caused the error.\n     * @return {boolean} Whether or not the stack information was\n     * augmented.\n     */\n    function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {\n        var initial = {\n            'url': url,\n            'line': lineNo\n        };\n\n        if (initial.url && initial.line) {\n            stackInfo.incomplete = false;\n\n            if (!initial.func) {\n                initial.func = guessFunctionName(initial.url, initial.line);\n            }\n\n            if (!initial.context) {\n                initial.context = gatherContext(initial.url, initial.line);\n            }\n\n            var reference = / '([^']+)' /.exec(message);\n            if (reference) {\n                initial.column = findSourceInLine(reference[1], initial.url, initial.line);\n            }\n\n            if (stackInfo.stack.length > 0) {\n                if (stackInfo.stack[0].url === initial.url) {\n                    if (stackInfo.stack[0].line === initial.line) {\n                        return false; // already in stack trace\n                    } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {\n                        stackInfo.stack[0].line = initial.line;\n                        stackInfo.stack[0].context = initial.context;\n                        return false;\n                    }\n                }\n            }\n\n            stackInfo.stack.unshift(initial);\n            stackInfo.partial = true;\n            return true;\n        } else {\n            stackInfo.incomplete = true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Computes stack trace information by walking the arguments.caller\n     * chain at the time the exception occurred. This will cause earlier\n     * frames to be missed but is the only way to get any stack trace in\n     * Safari and IE. The top frame is restored by\n     * {@link augmentStackTraceWithInitialElement}.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceByWalkingCallerChain(ex, depth) {\n        var functionName = /function\\s+([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*)?\\s*\\(/i,\n            stack = [],\n            funcs = {},\n            recursion = false,\n            parts,\n            item,\n            source;\n\n        for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {\n            if (curr === computeStackTrace || curr === TraceKit.report) {\n                // console.log('skipping internal function');\n                continue;\n            }\n\n            item = {\n                'url': null,\n                'func': UNKNOWN_FUNCTION,\n                'args': [],\n                'line': null,\n                'column': null\n            };\n\n            if (curr.name) {\n                item.func = curr.name;\n            } else if ((parts = functionName.exec(curr.toString()))) {\n                item.func = parts[1];\n            }\n\n            if (typeof item.func === 'undefined') {\n              try {\n                item.func = parts.input.substring(0, parts.input.indexOf('{'));\n              } catch (e) { }\n            }\n\n            if ((source = findSourceByFunctionBody(curr))) {\n                item.url = source.url;\n                item.line = source.line;\n\n                if (item.func === UNKNOWN_FUNCTION) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n\n                var reference = / '([^']+)' /.exec(ex.message || ex.description);\n                if (reference) {\n                    item.column = findSourceInLine(reference[1], source.url, source.line);\n                }\n            }\n\n            if (funcs['' + curr]) {\n                recursion = true;\n            }else{\n                funcs['' + curr] = true;\n            }\n\n            stack.push(item);\n        }\n\n        if (depth) {\n            // console.log('depth is ' + depth);\n            // console.log('stack is ' + stack.length);\n            stack.splice(0, depth);\n        }\n\n        var result = {\n            'mode': 'callers',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n        augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);\n        return result;\n    }\n\n    /**\n     * Computes a stack trace for an exception.\n     * @param {Error} ex\n     * @param {(string|number)=} depth\n     */\n    function computeStackTrace(ex, depth) {\n        var stack = null;\n        depth = (depth == null ? 0 : +depth);\n\n        try {\n            // This must be tried first because Opera 10 *destroys*\n            // its stacktrace property if you try to access the stack\n            // property first!!\n            stack = computeStackTraceFromStacktraceProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromStackProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromOperaMultiLineMessage(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        return {\n            'mode': 'failed'\n        };\n    }\n\n    /**\n     * Logs a stacktrace starting from the previous call and working down.\n     * @param {(number|string)=} depth How many frames deep to trace.\n     * @return {Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceOfCaller(depth) {\n        depth = (depth == null ? 0 : +depth) + 1; // \"+ 1\" because \"ofCaller\" should drop one frame\n        try {\n            throw new Error();\n        } catch (ex) {\n            return computeStackTrace(ex, depth + 1);\n        }\n    }\n\n    computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;\n    computeStackTrace.guessFunctionName = guessFunctionName;\n    computeStackTrace.gatherContext = gatherContext;\n    computeStackTrace.ofCaller = computeStackTraceOfCaller;\n    computeStackTrace.getSource = getSource;\n\n    return computeStackTrace;\n}());\n\n/**\n * Extends support for global error handling for asynchronous browser\n * functions. Adopted from Closure Library's errorhandler.js\n */\nTraceKit.extendToAsynchronousCallbacks = function () {\n    var _helper = function _helper(fnName) {\n        var originalFn = window[fnName];\n        window[fnName] = function traceKitAsyncExtension() {\n            // Make a copy of the arguments\n            var args = _slice.call(arguments);\n            var originalCallback = args[0];\n            if (typeof (originalCallback) === 'function') {\n                args[0] = TraceKit.wrap(originalCallback);\n            }\n            // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it\n            // also only supports 2 argument and doesn't care what \"this\" is, so we\n            // can just call the original function directly.\n            if (originalFn.apply) {\n                return originalFn.apply(this, args);\n            } else {\n                return originalFn(args[0], args[1]);\n            }\n        };\n    };\n\n    _helper('setTimeout');\n    _helper('setInterval');\n};\n\n//Default options:\nif (!TraceKit.remoteFetching) {\n  TraceKit.remoteFetching = true;\n}\nif (!TraceKit.collectWindowErrors) {\n  TraceKit.collectWindowErrors = true;\n}\nif (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {\n  // 5 lines before, the offending line, 5 lines after\n  TraceKit.linesOfContext = 11;\n}\n\n\n\n// Export to global object\nwindow.TraceKit = TraceKit;\n\n}(typeof window !== 'undefined' ? window : global));\n","export interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority:number = 30;\n  public name:string = 'ErrorPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\n                                                                                                                                                                                                                                                                     \n\nexport class DefaultErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getParameters(parameters:string|string[]): IParameter[] {\n      let params:string[] = (typeof parameters === 'string' ? [parameters] : parameters) || [];\n\n      let result:IParameter[] = [];\n      for (let index = 0; index < params.length; index++) {\n        result.push({ name: params[index] });\n      }\n\n      return result;\n    }\n\n    function getStackFrames(stackFrames:TraceKit.StackFrame[]): IStackFrame[] {\n      const ANONYMOUS:string = '<anonymous>';\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: (frame.func || ANONYMOUS).replace('?', ANONYMOUS),\n          parameters: getParameters(frame.args),\n          file_name: frame.url,\n          line_number: frame.line || 0,\n          column: frame.column || 0\n        });\n      }\n\n      return frames;\n    }\n\n    const TRACEKIT_STACK_TRACE_KEY:string = '@@_TraceKit.StackTrace'; // optimization for minifier.\n\n    let stackTrace:TraceKit.StackTrace = !!context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      ? context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      : TraceKit.computeStackTrace(exception, 25);\n\n    if (!stackTrace) {\n      throw new Error('Unable to parse the exceptions stack trace.');\n    }\n\n    return {\n      type: stackTrace.name,\n      message: stackTrace.message || exception.message,\n      stack_trace: getStackFrames(stackTrace.stack || [])\n    };\n  }\n}\n\n                                                                                                                                                                                                         \n\nexport class DefaultModuleCollector implements IModuleCollector {\n  public getModules(context:EventPluginContext): IModule[] {\n    if (document && document.getElementsByTagName) {\n      return null;\n    }\n\n    let modules:IModule[] = [];\n    let scripts = document.getElementsByTagName('script');\n    if (scripts && scripts.length > 0) {\n      for (let index = 0; index < scripts.length; index++) {\n        if (scripts[index].src) {\n          modules.push({\n            module_id: index,\n            name: scripts[index].src,\n            version: Utils.parseVersion(scripts[index].src)\n          });\n        } else if (!!scripts[index].innerHTML) {\n          modules.push({\n            module_id: index,\n            name: 'Script Tag',\n            version: Utils.getHashCode(scripts[index].innerHTML)\n          });\n        }\n      }\n    }\n\n    return modules;\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class DefaultRequestInfoCollector implements IRequestInfoCollector {\n  public getRequestInfo(context:EventPluginContext): IRequestInfo {\n    if (!document || !navigator || !location) {\n      return null;\n    }\n\n    let requestInfo:IRequestInfo = {\n      user_agent: navigator.userAgent,\n      is_secure: location.protocol === 'https:',\n      host: location.hostname,\n      port: location.port && location.port !== '' ? parseInt(location.port, 10) : 80,\n      path: location.pathname,\n      // client_ip_address: 'TODO',\n      cookies: Utils.getCookies(document.cookie),\n      query_string: Utils.parseQueryString(location.search.substring(1))\n    };\n\n    if (document.referrer && document.referrer !== '') {\n      requestInfo.referrer = document.referrer;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                              \n\ndeclare var XDomainRequest: { new (); create(); };\n\nexport class DefaultSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request: SubmissionRequest, callback: SubmissionCallback, isAppExiting?:boolean) {\n    // TODO: Handle sending events when app is exiting with send beacon.\n    const TIMEOUT: string = 'timeout';  // optimization for minifier.\n    const LOADED: string = 'loaded';  // optimization for minifier.\n    const WITH_CREDENTIALS: string = 'withCredentials';  // optimization for minifier.\n\n    let isCompleted: boolean = false;\n    let useSetTimeout: boolean = false;\n    function complete(mode: string, xhr: XMLHttpRequest) {\n      function parseResponseHeaders(headerStr) {\n        function trim(value) {\n          return value.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n\n        let headers = {};\n        let headerPairs = (headerStr || '').split('\\u000d\\u000a');\n        for (let index: number = 0; index < headerPairs.length; index++) {\n          let headerPair = headerPairs[index];\n          // Can't use split() here because it does the wrong thing\n          // if the header value has the string \": \" in it.\n          let separator = headerPair.indexOf('\\u003a\\u0020');\n          if (separator > 0) {\n            headers[trim(headerPair.substring(0, separator).toLowerCase())] = headerPair.substring(separator + 2);\n          }\n        }\n\n        return headers;\n      }\n\n      if (isCompleted) {\n        return;\n      }\n\n      isCompleted = true;\n\n      let message: string = xhr.statusText;\n      let responseText: string = xhr.responseText;\n      let status: number = xhr.status;\n\n      if (mode === TIMEOUT || status === 0) {\n        message = 'Unable to connect to server.';\n        status = 0;\n      } else if (mode === LOADED && !status) {\n        status = request.method === 'POST' ? 202 : 200;\n      } else if (status < 200 || status > 299) {\n        let responseBody: any = xhr.responseBody;\n        if (!!responseBody && !!responseBody.message) {\n          message = responseBody.message;\n        } else if (!!responseText && responseText.indexOf('message') !== -1) {\n          try {\n            message = JSON.parse(responseText).message;\n          } catch (e) {\n            message = responseText;\n          }\n        }\n      }\n\n      callback(status || 500, message || '', responseText, parseResponseHeaders(xhr.getAllResponseHeaders && xhr.getAllResponseHeaders()));\n    }\n\n    function createRequest(userAgent:string, method: string, url: string): XMLHttpRequest {\n      let xhr: any = new XMLHttpRequest();\n      if (WITH_CREDENTIALS in xhr) {\n        xhr.open(method, url, true);\n\n        xhr.setRequestHeader('X-Exceptionless-Client', userAgent);\n        if (method === 'POST') {\n          xhr.setRequestHeader('Content-Type', 'application/json');\n        }\n      } else if (typeof XDomainRequest !== 'undefined') {\n        useSetTimeout = true;\n        xhr = new XDomainRequest();\n        xhr.open(method, location.protocol === 'http:' ? url.replace('https:', 'http:') : url);\n      } else {\n        xhr = null;\n      }\n\n      if (xhr) {\n        xhr.timeout = 10000;\n      }\n\n      return xhr;\n    }\n\n    let url = `${request.serverUrl}${request.path}?access_token=${encodeURIComponent(request.apiKey) }`;\n    let xhr = createRequest(request.userAgent, request.method || 'POST', url);\n    if (!xhr) {\n      return callback(503, 'CORS not supported.');\n    }\n\n    if (WITH_CREDENTIALS in xhr) {\n      xhr.onreadystatechange = () => {\n        // xhr not ready.\n        if (xhr.readyState !== 4) {\n          return;\n        }\n\n        complete(LOADED, xhr);\n      };\n    }\n\n    xhr.onprogress = () => {};\n    xhr.ontimeout = () => complete(TIMEOUT, xhr);\n    xhr.onerror = () => complete('error', xhr);\n    xhr.onload = () => complete(LOADED, xhr);\n\n    if (useSetTimeout) {\n      setTimeout(() => xhr.send(request.data), 500);\n    } else {\n      xhr.send(request.data);\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n\nfunction getDefaultsSettingsFromScriptTag(): IConfigurationSettings {\n  if (!document || !document.getElementsByTagName) {\n    return null;\n  }\n\n  let scripts = document.getElementsByTagName('script');\n  for (let index = 0; index < scripts.length; index++) {\n    if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) {\n      return Utils.parseQueryString(scripts[index].src.split('?').pop());\n    }\n  }\n  return null;\n}\n\nfunction processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void {\n  let builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror');\n  builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace;\n  builder.submit();\n}\n\n/*\nTODO: We currently are unable to parse string exceptions.\nfunction processJQueryAjaxError(event, xhr, settings, error:string): void {\n  let client = ExceptionlessClient.default;\n  if (xhr.status === 404) {\n    client.submitNotFound(settings.url);\n  } else if (xhr.status !== 401) {\n    client.createUnhandledException(error, 'JQuery.ajaxError')\n      .setSource(settings.url)\n      .setProperty('status', xhr.status)\n      .setProperty('request', settings.data)\n      .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024))\n      .submit();\n  }\n}\n*/\n\nlet defaults = Configuration.defaults;\nlet settings = getDefaultsSettingsFromScriptTag();\nif (settings && (settings.apiKey || settings.serverUrl)) {\n  defaults.apiKey = settings.apiKey;\n  defaults.serverUrl = settings.serverUrl;\n}\n\ndefaults.errorParser = new DefaultErrorParser();\ndefaults.moduleCollector = new DefaultModuleCollector();\ndefaults.requestInfoCollector = new DefaultRequestInfoCollector();\ndefaults.submissionAdapter = new DefaultSubmissionAdapter();\n\nTraceKit.report.subscribe(processUnhandledException);\nTraceKit.extendToAsynchronousCallbacks();\n\n// window && window.addEventListener && window.addEventListener('beforeunload', function () {\n//   ExceptionlessClient.default.config.queue.process(true);\n// });\n\n// if (typeof $ !== 'undefined' && $(document)) {\n//   $(document).ajaxError(processJQueryAjaxError);\n// }\n\n(<any>Error).stackTraceLimit = Infinity;\n\ndeclare var $;\n\n",null],"sourceRoot":"/source/"}
\ No newline at end of file
+{"version":3,"sources":["tracekit.js","/source/exceptionless.ts","exceptionless.min.js"],"names":["window","undefined","_has","object","key","Object","prototype","hasOwnProperty","call","_isUndefined","what","TraceKit","_oldTraceKit","_slice","slice","UNKNOWN_FUNCTION","noConflict","wrap","func","wrapped","apply","this","arguments","e","report","subscribe","handler","installGlobalHandler","handlers","push","unsubscribe","i","length","splice","notifyHandlers","stack","isWindowError","exception","collectWindowErrors","concat","inner","traceKitWindowOnError","message","url","lineNo","columnNo","errorObj","computeStackTrace","lastExceptionStack","augmentStackTraceWithInitialElement","lastException","location","line","column","guessFunctionName","context","gatherContext","mode","_oldOnerrorHandler","_onErrorHandlerInstalled","onerror","ex","args","s","setTimeout","incomplete","loadSource","remoteFetching","getXHR","XMLHttpRequest","ActiveXObject","request","open","send","responseText","getSource","sourceCache","source","domain","document","indexOf","split","m","reFunctionArgNames","reGuessFunction","maxLines","exec","linesBefore","Math","floor","linesOfContext","linesAfter","start","max","end","min","escapeRegExp","text","replace","escapeCodeAsRegExpForMatchingInsideHTML","body","findSourceInUrls","re","urls","j","join","substring","index","lastIndexOf","findSourceInLine","fragment","RegExp","findSourceByFunctionBody","parts","result","href","scripts","getElementsByTagName","code","codeRE","eventRE","script","src","name","event","computeStackTraceFromStackProp","element","chrome","gecko","winjs","lines","reference","isNative","columnNumber","computeStackTraceFromStacktraceProp","stacktrace","opera10Regex","opera11Regex","exc","computeStackTraceFromOperaMultiLineMessage","lineRE1","lineRE2","lineRE3","inlineScriptBlocks","item","relativeLine","pos","innerText","midline","stackInfo","initial","unshift","partial","computeStackTraceByWalkingCallerChain","depth","functionName","funcs","recursion","curr","caller","toString","input","description","sourceURL","fileName","lineNumber","debug","computeStackTraceOfCaller","Error","ofCaller","extendToAsynchronousCallbacks","_helper","fnName","originalFn","originalCallback","global","root","factory","define","amd","exports","module","require","exceptionless","getDefaultsSettingsFromScriptTag","Utils","parseQueryString","pop","processUnhandledException","stackTrace","options","builder","ExceptionlessClient","createUnhandledException","status","pluginContextData","submit","SettingsManager","_handlers","changed","config","checkVersion","version","savedConfigVersion","parseInt","storage","get","_configPath","isNaN","log","info","updateSettings","_this","isValid","submissionClient","getSettings","response","success","settings","merge","savedServerSettings","getSavedServerSettings","path","save","settingsVersion","error","DefaultLastReferenceIdManager","_lastReferenceId","getLast","clearLast","setLast","eventId","ConsoleLog","warn","level","console","NullLog","EventPluginContext","client","contextData","ContextData","defineProperty","enumerable","configurable","EventPluginManager","run","callback","plugin","next","cancelled","plugins","wrappedPlugins","priority","addDefaultPlugins","addPlugin","ConfigurationDefaultsPlugin","ErrorPlugin","ModuleInfoPlugin","RequestInfoPlugin","EnvironmentInfoPlugin","SubmissionMethodPlugin","ReferenceIdPlugin","reference_id","type","guid","DefaultEventQueue","_processingQueue","_config","enqueue","ensureQueueTimer","areQueuedItemsDiscarded","Date","toJSON","randomNumber","process","isAppExiting","getEvents","events","items","value","queueNotProcessed","enabled","getList","submissionBatchSize","serverUrl","postEvents","processSubmissionResponse","suspendProcessing","durationInMinutes","discardFutureQueuedItems","clearQueue","_suspendProcessingUntil","getTime","_discardQueuedItemsUntil","removeEvents","_queueTimer","setInterval","onProcessQueue","isQueueProcessingSuspended","noSubmission","serviceUnavailable","paymentRequired","unableToAuthenticate","notFound","badRequest","requestEntityTooLarge","round","remove","InMemoryStorage","maxItems","_items","_maxItems","created","shift","searchPattern","limit","regex","results","test","DefaultSubmissionClient","configurationVersionHeader","data","stringify","dataExclusions","createRequest","cb","createSubmissionCallback","submissionAdapter","sendRequest","postUserDescription","referenceId","encodeURIComponent","headers","SettingsResponse","JSON","parse","method","apiKey","userAgent","SubmissionResponse","addRange","target","values","_i","getHashCode","hash","character","charCodeAt","getCookies","cookies","cookie","s4","random","defaultValues","parseVersion","versionRegex","matches","query","pairs","pair","decodeURIComponent","exclusions","checkForMatch","pattern","trim","toLowerCase","startsWithWildcard","endsWithWildcard","stringifyImpl","obj","excludedKeys","cache","Configuration","configSettings","inject","fn","defaultTags","defaultData","lastReferenceIdManager","_plugins","_serverUrl","_dataExclusions","defaults","environmentInfoCollector","errorParser","moduleCollector","requestInfoCollector","queue","applySavedServerSettings","_apiKey","set","addDataExclusions","sort","p1","p2","pluginOrName","pluginAction","pluginExists","removePlugin","setVersion","setUserIdentity","userInfoOrIdentity","USER_KEY","userInfo","identity","shouldRemove","useReferenceIds","useDebugLogger","_defaultSettings","EventBuilder","_validIdentifierErrorMessage","setType","setSource","setSessionId","sessionId","isValidIdentifier","session_id","setReferenceId","setMessage","setGeo","latitude","longitude","geo","setProperty","setValue","addTags","tags","markAsCritical","critical","addRequestInfo","submitEvent","isDigit","isLetter","isMinus","setException","getException","markAsUnhandledError","setSubmissionMethod","getSubmissionMethod","statusCode","settingsOrApiKey","createException","createEvent","submitException","submissionMethod","submitUnhandledException","createFeatureUsage","feature","submitFeatureUsage","createLog","sourceOrMessage","callee","submitLog","createNotFound","resource","submitNotFound","createSessionStart","submitSessionStart","createSessionEnd","submitSessionEnd","date","ctx","ev","updateUserEmailAndDescription","email","userDescription","email_address","getLastReferenceId","_instance","tag","ignoredProperties","ERROR_KEY","EXTRA_PROPERTIES_KEY","parser","additionalData","getAdditionalData","keys","filter","forEach","collector","modules","getModules","REQUEST_KEY","requestInfo","getRequestInfo","ENVIRONMENT_KEY","environmentInfo","getEnvironmentInfo","DefaultErrorParser","getParameters","parameters","params","getStackFrames","stackFrames","ANONYMOUS","frames","frame","file_name","line_number","TRACEKIT_STACK_TRACE_KEY","stack_trace","DefaultModuleCollector","module_id","innerHTML","DefaultRequestInfoCollector","navigator","user_agent","is_secure","protocol","host","hostname","port","pathname","query_string","search","referrer","DefaultSubmissionAdapter","complete","xhr","parseResponseHeaders","headerStr","headerPairs","headerPair","separator","isCompleted","statusText","TIMEOUT","LOADED","responseBody","getAllResponseHeaders","WITH_CREDENTIALS","setRequestHeader","XDomainRequest","useSetTimeout","timeout","onreadystatechange","readyState","onprogress","ontimeout","onload","stackTraceLimit","Infinity"],"mappings":"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","file":"exceptionless.min.js","sourcesContent":["/*\n TraceKit - Cross browser stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n    return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n    return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n    return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n    window.TraceKit = _oldTraceKit;\n    return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n    function wrapped() {\n        try {\n            return func.apply(this, arguments);\n        } catch (e) {\n            TraceKit.report(e);\n            throw e;\n        }\n    }\n    return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n *   TraceKit.report.subscribe(function(stackInfo) { ... })\n *   TraceKit.report.unsubscribe(function(stackInfo) { ... })\n *   TraceKit.report(exception)\n *   try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n *   - Firefox: full stack trace with line numbers, plus column number\n *              on top frame; column number is not guaranteed\n *   - Opera:   full stack trace with line and column numbers\n *   - Chrome:  full stack trace with line and column numbers\n *   - Safari:  line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *   - IE:      line and column number for the top frame only; some frames\n *              may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n *   - IE5.5+ (only 8.0 tested)\n *   - Firefox 0.9+ (only 3.5+ tested)\n *   - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n *     Exceptions Have Stacktrace to be enabled in opera:config)\n *   - Safari 3+ (only 4+ tested)\n *   - Chrome 1+ (only 5+ tested)\n *   - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n    var handlers = [],\n        lastException = null,\n        lastExceptionStack = null;\n\n    /**\n     * Add a crash handler.\n     * @param {Function} handler\n     */\n    function subscribe(handler) {\n        installGlobalHandler();\n        handlers.push(handler);\n    }\n\n    /**\n     * Remove a crash handler.\n     * @param {Function} handler\n     */\n    function unsubscribe(handler) {\n        for (var i = handlers.length - 1; i >= 0; --i) {\n            if (handlers[i] === handler) {\n                handlers.splice(i, 1);\n            }\n        }\n    }\n\n    /**\n     * Dispatch stack information to all handlers.\n     * @param {Object.<string, *>} stack\n     */\n    function notifyHandlers(stack, isWindowError) {\n        var exception = null;\n        if (isWindowError && !TraceKit.collectWindowErrors) {\n          return;\n        }\n        for (var i in handlers) {\n            if (_has(handlers, i)) {\n                try {\n                    handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n                } catch (inner) {\n                    exception = inner;\n                }\n            }\n        }\n\n        if (exception) {\n            throw exception;\n        }\n    }\n\n    var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n    /**\n     * Ensures all global unhandled exceptions are recorded.\n     * Supported by Gecko and IE.\n     * @param {string} message Error message.\n     * @param {string} url URL of script that generated the exception.\n     * @param {(number|string)} lineNo The line number at which the error\n     * occurred.\n     * @param {?(number|string)} columnNo The column number at which the error\n     * occurred.\n     * @param {?Error} errorObj The actual Error object.\n     */\n    function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n        var stack = null;\n\n        if (errorObj) {\n          stack = TraceKit.computeStackTrace(errorObj);\n        }\n        else\n        {\n            if (lastExceptionStack) {\n                TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n                stack = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n            } else {\n                var location = {\n                    'url': url,\n                    'line': lineNo,\n                    'column': columnNo\n                };\n                location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n                location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n                stack = {\n                    'mode': 'onerror',\n                    'message': message,\n                    'stack': [location]\n                };\n            }\n        }\n\n        notifyHandlers(stack, 'from window.onerror');\n\n        if (_oldOnerrorHandler) {\n            return _oldOnerrorHandler.apply(this, arguments);\n        }\n\n        return false;\n    }\n\n    function installGlobalHandler ()\n    {\n        if (_onErrorHandlerInstalled === true) {\n            return;\n        }\n        _oldOnerrorHandler = window.onerror;\n        window.onerror = traceKitWindowOnError;\n        _onErrorHandlerInstalled = true;\n    }\n\n    /**\n     * Reports an unhandled Error to TraceKit.\n     * @param {Error} ex\n     */\n    function report(ex) {\n        var args = _slice.call(arguments, 1);\n        if (lastExceptionStack) {\n            if (lastException === ex) {\n                return; // already caught by an inner catch block, ignore\n            } else {\n                var s = lastExceptionStack;\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [s, null].concat(args));\n            }\n        }\n\n        var stack = TraceKit.computeStackTrace(ex);\n        lastExceptionStack = stack;\n        lastException = ex;\n\n        // If the stack trace is incomplete, wait for 2 seconds for\n        // slow slow IE to see if onerror occurs or not before reporting\n        // this exception; otherwise, we will end up with an incomplete\n        // stack trace\n        window.setTimeout(function () {\n            if (lastException === ex) {\n                lastExceptionStack = null;\n                lastException = null;\n                notifyHandlers.apply(null, [stack, null].concat(args));\n            }\n        }, (stack.incomplete ? 2000 : 0));\n\n        throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n    }\n\n    report.subscribe = subscribe;\n    report.unsubscribe = unsubscribe;\n    return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n *   s = TraceKit.computeStackTrace.ofCaller([depth])\n *   s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n *   s.name              - exception name\n *   s.message           - exception message\n *   s.stack[i].url      - JavaScript or HTML file URL\n *   s.stack[i].func     - function name, or empty for anonymous functions (if guessing did not work)\n *   s.stack[i].args     - arguments passed to the function, if known\n *   s.stack[i].line     - line number, if known\n *   s.stack[i].column   - column number, if known\n *   s.stack[i].context  - an array of source code lines; the middle element corresponds to the correct line#\n *   s.mode              - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n *   - Firefox:  full stack trace with line numbers and unreliable column\n *               number on top frame\n *   - Opera 10: full stack trace with line and column numbers\n *   - Opera 9-: full stack trace with line numbers\n *   - Chrome:   full stack trace with line and column numbers\n *   - Safari:   line and column number for the topmost stacktrace element\n *               only\n *   - IE:       no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n *  * in Safari, some methods may be missing from the stack trace;\n *  * in IE, the topmost function in the stack trace will always be the\n *    caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n *     function trace(message) {\n *         var stackInfo = TraceKit.computeStackTrace.ofCaller();\n *         var data = message + \"\\n\";\n *         for(var i in stackInfo.stack) {\n *             var item = stackInfo.stack[i];\n *             data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n *         }\n *         if (window.console)\n *             console.info(data);\n *         else\n *             alert(data);\n *     }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n    var debug = false,\n        sourceCache = {};\n\n    /**\n     * Attempts to retrieve source code via XMLHttpRequest, which is used\n     * to look up anonymous function names.\n     * @param {string} url URL of source code.\n     * @return {string} Source contents.\n     */\n    function loadSource(url) {\n        if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n            return '';\n        }\n        try {\n            var getXHR = function() {\n                try {\n                    return new window.XMLHttpRequest();\n                } catch (e) {\n                    // explicitly bubble up the exception if not found\n                    return new window.ActiveXObject('Microsoft.XMLHTTP');\n                }\n            };\n\n            var request = getXHR();\n            request.open('GET', url, false);\n            request.send('');\n            return request.responseText;\n        } catch (e) {\n            return '';\n        }\n    }\n\n    /**\n     * Retrieves source code from the source code cache.\n     * @param {string} url URL of source code.\n     * @return {Array.<string>} Source contents.\n     */\n    function getSource(url) {\n        if (typeof url !== 'string') {\n            return [];\n        }\n\n        if (!_has(sourceCache, url)) {\n            // URL needs to be able to fetched within the acceptable domain.  Otherwise,\n            // cross-domain errors will be triggered.\n            var source = '';\n\n            var domain = '';\n            try { domain = document.domain; } catch (e) {}\n            if (url.indexOf(domain) !== -1) {\n                source = loadSource(url);\n            }\n            sourceCache[url] = source ? source.split('\\n') : [];\n        }\n\n        return sourceCache[url];\n    }\n\n    /**\n     * Tries to use an externally loaded copy of source code to determine\n     * the name of a function by looking at the name of the variable it was\n     * assigned to, if any.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} lineNo Line number in source code.\n     * @return {string} The function name, if discoverable.\n     */\n    function guessFunctionName(url, lineNo) {\n        var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n            reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n            line = '',\n            maxLines = 10,\n            source = getSource(url),\n            m;\n\n        if (!source.length) {\n            return UNKNOWN_FUNCTION;\n        }\n\n        // Walk backwards from the first line in the function until we find the line which\n        // matches the pattern above, which is the function definition\n        for (var i = 0; i < maxLines; ++i) {\n            line = source[lineNo - i] + line;\n\n            if (!_isUndefined(line)) {\n                if ((m = reGuessFunction.exec(line))) {\n                    return m[1];\n                } else if ((m = reFunctionArgNames.exec(line))) {\n                    return m[1];\n                }\n            }\n        }\n\n        return UNKNOWN_FUNCTION;\n    }\n\n    /**\n     * Retrieves the surrounding lines from where an exception occurred.\n     * @param {string} url URL of source code.\n     * @param {(string|number)} line Line number in source code to centre\n     * around for context.\n     * @return {?Array.<string>} Lines of source code.\n     */\n    function gatherContext(url, line) {\n        var source = getSource(url);\n\n        if (!source.length) {\n            return null;\n        }\n\n        var context = [],\n            // linesBefore & linesAfter are inclusive with the offending line.\n            // if linesOfContext is even, there will be one extra line\n            //   *before* the offending line.\n            linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n            // Add one extra line if linesOfContext is odd\n            linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n            start = Math.max(0, line - linesBefore - 1),\n            end = Math.min(source.length, line + linesAfter - 1);\n\n        line -= 1; // convert to 0-based index\n\n        for (var i = start; i < end; ++i) {\n            if (!_isUndefined(source[i])) {\n                context.push(source[i]);\n            }\n        }\n\n        return context.length > 0 ? context : null;\n    }\n\n    /**\n     * Escapes special characters, except for whitespace, in a string to be\n     * used inside a regular expression as a string literal.\n     * @param {string} text The string.\n     * @return {string} The escaped string literal.\n     */\n    function escapeRegExp(text) {\n        return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n    }\n\n    /**\n     * Escapes special characters in a string to be used inside a regular\n     * expression as a string literal. Also ensures that HTML entities will\n     * be matched the same as their literal friends.\n     * @param {string} body The string.\n     * @return {string} The escaped string.\n     */\n    function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n        return escapeRegExp(body).replace('<', '(?:<|&lt;)').replace('>', '(?:>|&gt;)').replace('&', '(?:&|&amp;)').replace('\"', '(?:\"|&quot;)').replace(/\\s+/g, '\\\\s+');\n    }\n\n    /**\n     * Determines where a code fragment occurs in the source code.\n     * @param {RegExp} re The function definition.\n     * @param {Array.<string>} urls A list of URLs to search.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceInUrls(re, urls) {\n        var source, m;\n        for (var i = 0, j = urls.length; i < j; ++i) {\n            // console.log('searching', urls[i]);\n            if ((source = getSource(urls[i])).length) {\n                source = source.join('\\n');\n                if ((m = re.exec(source))) {\n                    // console.log('Found function in ' + urls[i]);\n\n                    return {\n                        'url': urls[i],\n                        'line': source.substring(0, m.index).split('\\n').length,\n                        'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n                    };\n                }\n            }\n        }\n\n        // console.log('no match');\n\n        return null;\n    }\n\n    /**\n     * Determines at which column a code fragment occurs on a line of the\n     * source code.\n     * @param {string} fragment The code fragment.\n     * @param {string} url The URL to search.\n     * @param {(string|number)} line The line number to examine.\n     * @return {?number} The column number.\n     */\n    function findSourceInLine(fragment, url, line) {\n        var source = getSource(url),\n            re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n            m;\n\n        line -= 1;\n\n        if (source && source.length > line && (m = re.exec(source[line]))) {\n            return m.index;\n        }\n\n        return null;\n    }\n\n    /**\n     * Determines where a function was defined within the source code.\n     * @param {(Function|string)} func A function reference or serialized\n     * function definition.\n     * @return {?Object.<string, (string|number)>} An object containing\n     * the url, line, and column number of the defined function.\n     */\n    function findSourceByFunctionBody(func) {\n        var urls = [window.location.href],\n            scripts = document.getElementsByTagName('script'),\n            body,\n            code = '' + func,\n            codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n            re,\n            parts,\n            result;\n\n        for (var i = 0; i < scripts.length; ++i) {\n            var script = scripts[i];\n            if (script.src) {\n                urls.push(script.src);\n            }\n        }\n\n        if (!(parts = codeRE.exec(code))) {\n            re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n        }\n\n        // not sure if this is really necessary, but I don’t have a test\n        // corpus large enough to confirm that and it was in the original.\n        else {\n            var name = parts[1] ? '\\\\s+' + parts[1] : '',\n                args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n            body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n            re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n        }\n\n        // look for a normal function definition\n        if ((result = findSourceInUrls(re, urls))) {\n            return result;\n        }\n\n        // look for an old-school event handler function\n        if ((parts = eventRE.exec(code))) {\n            var event = parts[1];\n            body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n            // look for a function defined in HTML as an onXXX handler\n            re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n            if ((result = findSourceInUrls(re, urls[0]))) {\n                return result;\n            }\n\n            // look for ???\n            re = new RegExp(body);\n\n            if ((result = findSourceInUrls(re, urls))) {\n                return result;\n            }\n        }\n\n        return null;\n    }\n\n    // Contents of Exception in various browsers.\n    //\n    // SAFARI:\n    // ex.message = Can't find variable: qq\n    // ex.line = 59\n    // ex.sourceId = 580238192\n    // ex.sourceURL = http://...\n    // ex.expressionBeginOffset = 96\n    // ex.expressionCaretOffset = 98\n    // ex.expressionEndOffset = 98\n    // ex.name = ReferenceError\n    //\n    // FIREFOX:\n    // ex.message = qq is not defined\n    // ex.fileName = http://...\n    // ex.lineNumber = 59\n    // ex.columnNumber = 69\n    // ex.stack = ...stack trace... (see the example below)\n    // ex.name = ReferenceError\n    //\n    // CHROME:\n    // ex.message = qq is not defined\n    // ex.name = ReferenceError\n    // ex.type = not_defined\n    // ex.arguments = ['aa']\n    // ex.stack = ...stack trace...\n    //\n    // INTERNET EXPLORER:\n    // ex.message = ...\n    // ex.name = ReferenceError\n    //\n    // OPERA:\n    // ex.message = ...message... (see the example below)\n    // ex.name = ReferenceError\n    // ex.opera#sourceloc = 11  (pretty much useless, duplicates the info in ex.message)\n    // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n    /**\n     * Computes stack trace information from the stack property.\n     * Chrome and Gecko use this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStackProp(ex) {\n        if (!ex.stack) {\n            return null;\n        }\n\n        var chrome = /^\\s*at (.*?) ?\\(((?:file|https?|chrome-extension|native|eval).*?)(?::(\\d+))?(?::(\\d+))?\\)?\\s*$/i,\n            gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|chrome|\\[).*?)(?::(\\d+))?(?::(\\d+))?\\s*$/i,\n            winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|http|https):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n            lines = ex.stack.split('\\n'),\n            stack = [],\n            parts,\n            element,\n            reference = /^(.*) is undefined$/.exec(ex.message);\n\n        for (var i = 0, j = lines.length; i < j; ++i) {\n            if ((parts = chrome.exec(lines[i]))) {\n                var isNative = parts[2] && parts[2].indexOf('native') !== -1;\n                element = {\n                    'url': !isNative ? parts[2] : null,\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': isNative ? [parts[2]] : [],\n                    'line': parts[3] ? +parts[3] : null,\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = winjs.exec(lines[i]))) {\n                element = {\n                    'url': parts[2],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': [],\n                    'line': +parts[3],\n                    'column': parts[4] ? +parts[4] : null\n                };\n            } else if ((parts = gecko.exec(lines[i]))) {\n                element = {\n                    'url': parts[3],\n                    'func': parts[1] || UNKNOWN_FUNCTION,\n                    'args': parts[2] ? parts[2].split(',') : [],\n                    'line': parts[4] ? +parts[4] : null,\n                    'column': parts[5] ? +parts[5] : null\n                };\n            } else {\n                continue;\n            }\n\n            if (!element.func && element.line) {\n                element.func = guessFunctionName(element.url, element.line);\n            }\n\n            if (element.line) {\n                element.context = gatherContext(element.url, element.line);\n            }\n\n            stack.push(element);\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        if (stack[0] && stack[0].line && !stack[0].column && reference) {\n            stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n        } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n            // FireFox uses this awesome columnNumber property for its top frame\n            // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n            // so adding 1\n            stack[0].column = ex.columnNumber + 1;\n        }\n\n        return {\n            'mode': 'stack',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * Computes stack trace information from the stacktrace property.\n     * Opera 10+ uses this property.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceFromStacktraceProp(ex) {\n        // Access and store the stacktrace property before doing ANYTHING\n        // else to it because Opera is not very good at providing it\n        // reliably in other circumstances.\n        var stacktrace = ex.stacktrace;\n        if (!stacktrace) {\n            return;\n        }\n\n        var opera10Regex = / line (\\d+).*script (?:in )?(\\S+)(?:: in function (\\S+))?$/i,\n            opera11Regex = / line (\\d+), column (\\d+)\\s*(?:in (?:<anonymous function: ([^>]+)>|([^\\)]+))\\((.*)\\))? in (.*):\\s*$/i,\n            lines = stacktrace.split('\\n'),\n            stack = [],\n            parts;\n\n        for (var line = 0; line < lines.length; line += 2) {\n            var element = null;\n            if ((parts = opera10Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[2],\n                    'line': +parts[1],\n                    'column': null,\n                    'func': parts[3],\n                    'args':[]\n                };\n            } else if ((parts = opera11Regex.exec(lines[line]))) {\n                element = {\n                    'url': parts[6],\n                    'line': +parts[1],\n                    'column': +parts[2],\n                    'func': parts[3] || parts[4],\n                    'args': parts[5] ? parts[5].split(',') : []\n                };\n            }\n\n            if (element) {\n                if (!element.func && element.line) {\n                    element.func = guessFunctionName(element.url, element.line);\n                }\n                if (element.line) {\n                    try {\n                        element.context = gatherContext(element.url, element.line);\n                    } catch (exc) {}\n                }\n\n                if (!element.context) {\n                    element.context = [lines[line + 1]];\n                }\n\n                stack.push(element);\n            }\n        }\n\n        if (!stack.length) {\n            return null;\n        }\n\n        return {\n            'mode': 'stacktrace',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n    }\n\n    /**\n     * NOT TESTED.\n     * Computes stack trace information from an error message that includes\n     * the stack trace.\n     * Opera 9 and earlier use this method if the option to show stack\n     * traces is turned on in opera:config.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack information.\n     */\n    function computeStackTraceFromOperaMultiLineMessage(ex) {\n        // TODO: Clean this function up\n        // Opera includes a stack trace into the exception message. An example is:\n        //\n        // Statement on line 3: Undefined variable: undefinedFunc\n        // Backtrace:\n        //   Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n        //         undefinedFunc(a);\n        //   Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n        //           zzz(x, y, z);\n        //   Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n        //           yyy(a, a, a);\n        //   Line 1 of function script\n        //     try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n        //   ...\n\n        var lines = ex.message.split('\\n');\n        if (lines.length < 4) {\n            return null;\n        }\n\n        var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?)\\S+)(?:: in function (\\S+))?\\s*$/i,\n            lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n            stack = [],\n            scripts = document.getElementsByTagName('script'),\n            inlineScriptBlocks = [],\n            parts;\n\n        for (var s in scripts) {\n            if (_has(scripts, s) && !scripts[s].src) {\n                inlineScriptBlocks.push(scripts[s]);\n            }\n        }\n\n        for (var line = 2; line < lines.length; line += 2) {\n            var item = null;\n            if ((parts = lineRE1.exec(lines[line]))) {\n                item = {\n                    'url': parts[2],\n                    'func': parts[3],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null\n                };\n            } else if ((parts = lineRE2.exec(lines[line]))) {\n                item = {\n                    'url': parts[3],\n                    'func': parts[4],\n                    'args': [],\n                    'line': +parts[1],\n                    'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.\n                };\n                var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block\n                var script = inlineScriptBlocks[parts[2] - 1];\n                if (script) {\n                    var source = getSource(item.url);\n                    if (source) {\n                        source = source.join('\\n');\n                        var pos = source.indexOf(script.innerText);\n                        if (pos >= 0) {\n                            item.line = relativeLine + source.substring(0, pos).split('\\n').length;\n                        }\n                    }\n                }\n            } else if ((parts = lineRE3.exec(lines[line]))) {\n                var url = window.location.href.replace(/#.*$/, '');\n                var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1]));\n                var src = findSourceInUrls(re, [url]);\n                item = {\n                    'url': url,\n                    'func': '',\n                    'args': [],\n                    'line': src ? src.line : parts[1],\n                    'column': null\n                };\n            }\n\n            if (item) {\n                if (!item.func) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n                var context = gatherContext(item.url, item.line);\n                var midline = (context ? context[Math.floor(context.length / 2)] : null);\n                if (context && midline.replace(/^\\s*/, '') === lines[line + 1].replace(/^\\s*/, '')) {\n                    item.context = context;\n                } else {\n                    // if (context) alert(\"Context mismatch. Correct midline:\\n\" + lines[i+1] + \"\\n\\nMidline:\\n\" + midline + \"\\n\\nContext:\\n\" + context.join(\"\\n\") + \"\\n\\nURL:\\n\" + item.url);\n                    item.context = [lines[line + 1]];\n                }\n                stack.push(item);\n            }\n        }\n        if (!stack.length) {\n            return null; // could not parse multiline exception message as Opera stack trace\n        }\n\n        return {\n            'mode': 'multiline',\n            'name': ex.name,\n            'message': lines[0],\n            'stack': stack\n        };\n    }\n\n    /**\n     * Adds information about the first frame to incomplete stack traces.\n     * Safari and IE require this to get complete data on the first frame.\n     * @param {Object.<string, *>} stackInfo Stack trace information from\n     * one of the compute* methods.\n     * @param {string} url The URL of the script that caused an error.\n     * @param {(number|string)} lineNo The line number of the script that\n     * caused an error.\n     * @param {string=} message The error generated by the browser, which\n     * hopefully contains the name of the object that caused the error.\n     * @return {boolean} Whether or not the stack information was\n     * augmented.\n     */\n    function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) {\n        var initial = {\n            'url': url,\n            'line': lineNo\n        };\n\n        if (initial.url && initial.line) {\n            stackInfo.incomplete = false;\n\n            if (!initial.func) {\n                initial.func = guessFunctionName(initial.url, initial.line);\n            }\n\n            if (!initial.context) {\n                initial.context = gatherContext(initial.url, initial.line);\n            }\n\n            var reference = / '([^']+)' /.exec(message);\n            if (reference) {\n                initial.column = findSourceInLine(reference[1], initial.url, initial.line);\n            }\n\n            if (stackInfo.stack.length > 0) {\n                if (stackInfo.stack[0].url === initial.url) {\n                    if (stackInfo.stack[0].line === initial.line) {\n                        return false; // already in stack trace\n                    } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) {\n                        stackInfo.stack[0].line = initial.line;\n                        stackInfo.stack[0].context = initial.context;\n                        return false;\n                    }\n                }\n            }\n\n            stackInfo.stack.unshift(initial);\n            stackInfo.partial = true;\n            return true;\n        } else {\n            stackInfo.incomplete = true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Computes stack trace information by walking the arguments.caller\n     * chain at the time the exception occurred. This will cause earlier\n     * frames to be missed but is the only way to get any stack trace in\n     * Safari and IE. The top frame is restored by\n     * {@link augmentStackTraceWithInitialElement}.\n     * @param {Error} ex\n     * @return {?Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceByWalkingCallerChain(ex, depth) {\n        var functionName = /function\\s+([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*)?\\s*\\(/i,\n            stack = [],\n            funcs = {},\n            recursion = false,\n            parts,\n            item,\n            source;\n\n        for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) {\n            if (curr === computeStackTrace || curr === TraceKit.report) {\n                // console.log('skipping internal function');\n                continue;\n            }\n\n            item = {\n                'url': null,\n                'func': UNKNOWN_FUNCTION,\n                'args': [],\n                'line': null,\n                'column': null\n            };\n\n            if (curr.name) {\n                item.func = curr.name;\n            } else if ((parts = functionName.exec(curr.toString()))) {\n                item.func = parts[1];\n            }\n\n            if (typeof item.func === 'undefined') {\n              try {\n                item.func = parts.input.substring(0, parts.input.indexOf('{'));\n              } catch (e) { }\n            }\n\n            if ((source = findSourceByFunctionBody(curr))) {\n                item.url = source.url;\n                item.line = source.line;\n\n                if (item.func === UNKNOWN_FUNCTION) {\n                    item.func = guessFunctionName(item.url, item.line);\n                }\n\n                var reference = / '([^']+)' /.exec(ex.message || ex.description);\n                if (reference) {\n                    item.column = findSourceInLine(reference[1], source.url, source.line);\n                }\n            }\n\n            if (funcs['' + curr]) {\n                recursion = true;\n            }else{\n                funcs['' + curr] = true;\n            }\n\n            stack.push(item);\n        }\n\n        if (depth) {\n            // console.log('depth is ' + depth);\n            // console.log('stack is ' + stack.length);\n            stack.splice(0, depth);\n        }\n\n        var result = {\n            'mode': 'callers',\n            'name': ex.name,\n            'message': ex.message,\n            'stack': stack\n        };\n        augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);\n        return result;\n    }\n\n    /**\n     * Computes a stack trace for an exception.\n     * @param {Error} ex\n     * @param {(string|number)=} depth\n     */\n    function computeStackTrace(ex, depth) {\n        var stack = null;\n        depth = (depth == null ? 0 : +depth);\n\n        try {\n            // This must be tried first because Opera 10 *destroys*\n            // its stacktrace property if you try to access the stack\n            // property first!!\n            stack = computeStackTraceFromStacktraceProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromStackProp(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceFromOperaMultiLineMessage(ex);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        try {\n            stack = computeStackTraceByWalkingCallerChain(ex, depth + 1);\n            if (stack) {\n                return stack;\n            }\n        } catch (e) {\n            if (debug) {\n                throw e;\n            }\n        }\n\n        return {\n            'mode': 'failed'\n        };\n    }\n\n    /**\n     * Logs a stacktrace starting from the previous call and working down.\n     * @param {(number|string)=} depth How many frames deep to trace.\n     * @return {Object.<string, *>} Stack trace information.\n     */\n    function computeStackTraceOfCaller(depth) {\n        depth = (depth == null ? 0 : +depth) + 1; // \"+ 1\" because \"ofCaller\" should drop one frame\n        try {\n            throw new Error();\n        } catch (ex) {\n            return computeStackTrace(ex, depth + 1);\n        }\n    }\n\n    computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;\n    computeStackTrace.guessFunctionName = guessFunctionName;\n    computeStackTrace.gatherContext = gatherContext;\n    computeStackTrace.ofCaller = computeStackTraceOfCaller;\n    computeStackTrace.getSource = getSource;\n\n    return computeStackTrace;\n}());\n\n/**\n * Extends support for global error handling for asynchronous browser\n * functions. Adopted from Closure Library's errorhandler.js\n */\nTraceKit.extendToAsynchronousCallbacks = function () {\n    var _helper = function _helper(fnName) {\n        var originalFn = window[fnName];\n        window[fnName] = function traceKitAsyncExtension() {\n            // Make a copy of the arguments\n            var args = _slice.call(arguments);\n            var originalCallback = args[0];\n            if (typeof (originalCallback) === 'function') {\n                args[0] = TraceKit.wrap(originalCallback);\n            }\n            // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it\n            // also only supports 2 argument and doesn't care what \"this\" is, so we\n            // can just call the original function directly.\n            if (originalFn.apply) {\n                return originalFn.apply(this, args);\n            } else {\n                return originalFn(args[0], args[1]);\n            }\n        };\n    };\n\n    _helper('setTimeout');\n    _helper('setInterval');\n};\n\n//Default options:\nif (!TraceKit.remoteFetching) {\n  TraceKit.remoteFetching = true;\n}\nif (!TraceKit.collectWindowErrors) {\n  TraceKit.collectWindowErrors = true;\n}\nif (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) {\n  // 5 lines before, the offending line, 5 lines after\n  TraceKit.linesOfContext = 11;\n}\n\n\n\n// Export to global object\nwindow.TraceKit = TraceKit;\n\n}(typeof window !== 'undefined' ? window : global));\n","export interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority: number = 30;\n  public name: string = 'ErrorPlugin';\n  public ignoredProperties: string[] = [\n    'arguments',\n    'column',\n    'columnNumber',\n    'description',\n    'fileName',\n    'message',\n    'name',\n    'number',\n    'line',\n    'lineNumber',\n    'opera#sourceloc',\n    'sourceURL',\n    'stack',\n    'stacktrace'\n  ];\n\n  public run(context: EventPluginContext, next?: () => void): void {\n    const ERROR_KEY: string = '@error'; // optimization for minifier.\n    const EXTRA_PROPERTIES_KEY: string = '@ext';\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          let additionalData = this.getAdditionalData(exception);\n          if (!!additionalData) {\n            if (!result.data) {\n              result.data = {};\n            }\n            result.data[EXTRA_PROPERTIES_KEY] = additionalData;\n          }\n\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n\n  private getAdditionalData(exception: Error): { [key: string]: any } {\n    let keys = Object.keys(exception)\n      .filter(key => this.ignoredProperties.indexOf(key) < 0);\n\n    if (keys.length === 0) {\n      return null;\n    }\n\n    let additionalData = {};\n\n    keys.forEach(key => {\n      let value = exception[key];\n      if (typeof value !== 'function') {\n        additionalData[key] = value;\n      }\n    });\n\n    return additionalData;\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\n                                                                                                                                                                                                                                                                     \n\nexport class DefaultErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getParameters(parameters:string|string[]): IParameter[] {\n      let params:string[] = (typeof parameters === 'string' ? [parameters] : parameters) || [];\n\n      let result:IParameter[] = [];\n      for (let index = 0; index < params.length; index++) {\n        result.push({ name: params[index] });\n      }\n\n      return result;\n    }\n\n    function getStackFrames(stackFrames:TraceKit.StackFrame[]): IStackFrame[] {\n      const ANONYMOUS:string = '<anonymous>';\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: (frame.func || ANONYMOUS).replace('?', ANONYMOUS),\n          parameters: getParameters(frame.args),\n          file_name: frame.url,\n          line_number: frame.line || 0,\n          column: frame.column || 0\n        });\n      }\n\n      return frames;\n    }\n\n    const TRACEKIT_STACK_TRACE_KEY:string = '@@_TraceKit.StackTrace'; // optimization for minifier.\n\n    let stackTrace:TraceKit.StackTrace = !!context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      ? context.contextData[TRACEKIT_STACK_TRACE_KEY]\n      : TraceKit.computeStackTrace(exception, 25);\n\n    if (!stackTrace) {\n      throw new Error('Unable to parse the exceptions stack trace.');\n    }\n\n    return {\n      type: stackTrace.name,\n      message: stackTrace.message || exception.message,\n      stack_trace: getStackFrames(stackTrace.stack || [])\n    };\n  }\n}\n\n                                                                                                                                                                                                         \n\nexport class DefaultModuleCollector implements IModuleCollector {\n  public getModules(context:EventPluginContext): IModule[] {\n    if (document && document.getElementsByTagName) {\n      return null;\n    }\n\n    let modules:IModule[] = [];\n    let scripts = document.getElementsByTagName('script');\n    if (scripts && scripts.length > 0) {\n      for (let index = 0; index < scripts.length; index++) {\n        if (scripts[index].src) {\n          modules.push({\n            module_id: index,\n            name: scripts[index].src,\n            version: Utils.parseVersion(scripts[index].src)\n          });\n        } else if (!!scripts[index].innerHTML) {\n          modules.push({\n            module_id: index,\n            name: 'Script Tag',\n            version: Utils.getHashCode(scripts[index].innerHTML)\n          });\n        }\n      }\n    }\n\n    return modules;\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class DefaultRequestInfoCollector implements IRequestInfoCollector {\n  public getRequestInfo(context:EventPluginContext): IRequestInfo {\n    if (!document || !navigator || !location) {\n      return null;\n    }\n\n    let requestInfo:IRequestInfo = {\n      user_agent: navigator.userAgent,\n      is_secure: location.protocol === 'https:',\n      host: location.hostname,\n      port: location.port && location.port !== '' ? parseInt(location.port, 10) : 80,\n      path: location.pathname,\n      // client_ip_address: 'TODO',\n      cookies: Utils.getCookies(document.cookie),\n      query_string: Utils.parseQueryString(location.search.substring(1))\n    };\n\n    if (document.referrer && document.referrer !== '') {\n      requestInfo.referrer = document.referrer;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                              \n\ndeclare var XDomainRequest: { new (); create(); };\n\nexport class DefaultSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request: SubmissionRequest, callback: SubmissionCallback, isAppExiting?:boolean) {\n    // TODO: Handle sending events when app is exiting with send beacon.\n    const TIMEOUT: string = 'timeout';  // optimization for minifier.\n    const LOADED: string = 'loaded';  // optimization for minifier.\n    const WITH_CREDENTIALS: string = 'withCredentials';  // optimization for minifier.\n\n    let isCompleted: boolean = false;\n    let useSetTimeout: boolean = false;\n    function complete(mode: string, xhr: XMLHttpRequest) {\n      function parseResponseHeaders(headerStr) {\n        function trim(value) {\n          return value.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n        }\n\n        let headers = {};\n        let headerPairs = (headerStr || '').split('\\u000d\\u000a');\n        for (let index: number = 0; index < headerPairs.length; index++) {\n          let headerPair = headerPairs[index];\n          // Can't use split() here because it does the wrong thing\n          // if the header value has the string \": \" in it.\n          let separator = headerPair.indexOf('\\u003a\\u0020');\n          if (separator > 0) {\n            headers[trim(headerPair.substring(0, separator).toLowerCase())] = headerPair.substring(separator + 2);\n          }\n        }\n\n        return headers;\n      }\n\n      if (isCompleted) {\n        return;\n      }\n\n      isCompleted = true;\n\n      let message: string = xhr.statusText;\n      let responseText: string = xhr.responseText;\n      let status: number = xhr.status;\n\n      if (mode === TIMEOUT || status === 0) {\n        message = 'Unable to connect to server.';\n        status = 0;\n      } else if (mode === LOADED && !status) {\n        status = request.method === 'POST' ? 202 : 200;\n      } else if (status < 200 || status > 299) {\n        let responseBody: any = xhr.responseBody;\n        if (!!responseBody && !!responseBody.message) {\n          message = responseBody.message;\n        } else if (!!responseText && responseText.indexOf('message') !== -1) {\n          try {\n            message = JSON.parse(responseText).message;\n          } catch (e) {\n            message = responseText;\n          }\n        }\n      }\n\n      callback(status || 500, message || '', responseText, parseResponseHeaders(xhr.getAllResponseHeaders && xhr.getAllResponseHeaders()));\n    }\n\n    function createRequest(userAgent:string, method: string, url: string): XMLHttpRequest {\n      let xhr: any = new XMLHttpRequest();\n      if (WITH_CREDENTIALS in xhr) {\n        xhr.open(method, url, true);\n\n        xhr.setRequestHeader('X-Exceptionless-Client', userAgent);\n        if (method === 'POST') {\n          xhr.setRequestHeader('Content-Type', 'application/json');\n        }\n      } else if (typeof XDomainRequest !== 'undefined') {\n        useSetTimeout = true;\n        xhr = new XDomainRequest();\n        xhr.open(method, location.protocol === 'http:' ? url.replace('https:', 'http:') : url);\n      } else {\n        xhr = null;\n      }\n\n      if (xhr) {\n        xhr.timeout = 10000;\n      }\n\n      return xhr;\n    }\n\n    let url = `${request.serverUrl}${request.path}?access_token=${encodeURIComponent(request.apiKey) }`;\n    let xhr = createRequest(request.userAgent, request.method || 'POST', url);\n    if (!xhr) {\n      return callback(503, 'CORS not supported.');\n    }\n\n    if (WITH_CREDENTIALS in xhr) {\n      xhr.onreadystatechange = () => {\n        // xhr not ready.\n        if (xhr.readyState !== 4) {\n          return;\n        }\n\n        complete(LOADED, xhr);\n      };\n    }\n\n    xhr.onprogress = () => {};\n    xhr.ontimeout = () => complete(TIMEOUT, xhr);\n    xhr.onerror = () => complete('error', xhr);\n    xhr.onload = () => complete(LOADED, xhr);\n\n    if (useSetTimeout) {\n      setTimeout(() => xhr.send(request.data), 500);\n    } else {\n      xhr.send(request.data);\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n\nfunction getDefaultsSettingsFromScriptTag(): IConfigurationSettings {\n  if (!document || !document.getElementsByTagName) {\n    return null;\n  }\n\n  let scripts = document.getElementsByTagName('script');\n  for (let index = 0; index < scripts.length; index++) {\n    if (scripts[index].src && scripts[index].src.indexOf('/exceptionless') > -1) {\n      return Utils.parseQueryString(scripts[index].src.split('?').pop());\n    }\n  }\n  return null;\n}\n\nfunction processUnhandledException(stackTrace:TraceKit.StackTrace, options?:any): void {\n  let builder = ExceptionlessClient.default.createUnhandledException(new Error(stackTrace.message || (options || {}).status || 'Script error'), 'onerror');\n  builder.pluginContextData['@@_TraceKit.StackTrace'] = stackTrace;\n  builder.submit();\n}\n\n/*\nTODO: We currently are unable to parse string exceptions.\nfunction processJQueryAjaxError(event, xhr, settings, error:string): void {\n  let client = ExceptionlessClient.default;\n  if (xhr.status === 404) {\n    client.submitNotFound(settings.url);\n  } else if (xhr.status !== 401) {\n    client.createUnhandledException(error, 'JQuery.ajaxError')\n      .setSource(settings.url)\n      .setProperty('status', xhr.status)\n      .setProperty('request', settings.data)\n      .setProperty('response', xhr.responseText && xhr.responseText.slice && xhr.responseText.slice(0, 1024))\n      .submit();\n  }\n}\n*/\n\nlet defaults = Configuration.defaults;\nlet settings = getDefaultsSettingsFromScriptTag();\nif (settings && (settings.apiKey || settings.serverUrl)) {\n  defaults.apiKey = settings.apiKey;\n  defaults.serverUrl = settings.serverUrl;\n}\n\ndefaults.errorParser = new DefaultErrorParser();\ndefaults.moduleCollector = new DefaultModuleCollector();\ndefaults.requestInfoCollector = new DefaultRequestInfoCollector();\ndefaults.submissionAdapter = new DefaultSubmissionAdapter();\n\nTraceKit.report.subscribe(processUnhandledException);\nTraceKit.extendToAsynchronousCallbacks();\n\n// window && window.addEventListener && window.addEventListener('beforeunload', function () {\n//   ExceptionlessClient.default.config.queue.process(true);\n// });\n\n// if (typeof $ !== 'undefined' && $(document)) {\n//   $(document).ajaxError(processJQueryAjaxError);\n// }\n\n(<any>Error).stackTraceLimit = Infinity;\n\ndeclare var $;\n\n",null],"sourceRoot":"/source/"}
\ No newline at end of file
diff --git a/dist/exceptionless.node.js b/dist/exceptionless.node.js
index 75aa4e41..785bed83 100644
--- a/dist/exceptionless.node.js
+++ b/dist/exceptionless.node.js
@@ -1092,9 +1092,26 @@ var ErrorPlugin = (function () {
     function ErrorPlugin() {
         this.priority = 30;
         this.name = 'ErrorPlugin';
+        this.ignoredProperties = [
+            'arguments',
+            'column',
+            'columnNumber',
+            'description',
+            'fileName',
+            'message',
+            'name',
+            'number',
+            'line',
+            'lineNumber',
+            'opera#sourceloc',
+            'sourceURL',
+            'stack',
+            'stacktrace'
+        ];
     }
     ErrorPlugin.prototype.run = function (context, next) {
         var ERROR_KEY = '@error';
+        var EXTRA_PROPERTIES_KEY = '@ext';
         var exception = context.contextData.getException();
         if (!!exception) {
             context.event.type = 'error';
@@ -1105,12 +1122,35 @@ var ErrorPlugin = (function () {
                 }
                 var result = parser.parse(context, exception);
                 if (!!result) {
+                    var additionalData = this.getAdditionalData(exception);
+                    if (!!additionalData) {
+                        if (!result.data) {
+                            result.data = {};
+                        }
+                        result.data[EXTRA_PROPERTIES_KEY] = additionalData;
+                    }
                     context.event.data[ERROR_KEY] = result;
                 }
             }
         }
         next && next();
     };
+    ErrorPlugin.prototype.getAdditionalData = function (exception) {
+        var _this = this;
+        var keys = Object.keys(exception)
+            .filter(function (key) { return _this.ignoredProperties.indexOf(key) < 0; });
+        if (keys.length === 0) {
+            return null;
+        }
+        var additionalData = {};
+        keys.forEach(function (key) {
+            var value = exception[key];
+            if (typeof value !== 'function') {
+                additionalData[key] = value;
+            }
+        });
+        return additionalData;
+    };
     return ErrorPlugin;
 })();
 exports.ErrorPlugin = ErrorPlugin;
@@ -1204,6 +1244,7 @@ var SettingsResponse = (function () {
 exports.SettingsResponse = SettingsResponse;
 var os = require('os');
 var nodestacktrace = require('stack-trace');
+var path = require('path');
 var https = require('https');
 var url = require('url');
 var NodeEnvironmentInfoCollector = (function () {
@@ -1288,6 +1329,66 @@ var NodeErrorParser = (function () {
     return NodeErrorParser;
 })();
 exports.NodeErrorParser = NodeErrorParser;
+var NodeModuleCollector = (function () {
+    function NodeModuleCollector() {
+        this.initialized = false;
+        this.installedModules = {};
+    }
+    NodeModuleCollector.prototype.getModules = function (context) {
+        var _this = this;
+        this.initialize();
+        if (!require.main) {
+            return [];
+        }
+        var modulePath = path.dirname(require.main.filename) + '/node_modules/';
+        var pathLength = modulePath.length;
+        var loadedKeys = Object.keys(require.cache);
+        var loadedModules = {};
+        loadedKeys.forEach(function (key) {
+            var id = key.substr(pathLength);
+            id = id.substr(0, id.indexOf('/'));
+            loadedModules[id] = true;
+        });
+        return Object.keys(loadedModules)
+            .map(function (key) { return _this.installedModules[key]; })
+            .filter(function (m) { return m !== undefined; });
+    };
+    NodeModuleCollector.prototype.initialize = function () {
+        var _this = this;
+        if (this.initialized) {
+            return;
+        }
+        this.initialized = true;
+        var output = child.spawnSync('npm', ['ls', '--depth=0', '--json']).stdout;
+        if (!output) {
+            return;
+        }
+        var json;
+        try {
+            json = JSON.parse(output.toString());
+        }
+        catch (e) {
+            return;
+        }
+        var items = json.dependencies;
+        if (!items) {
+            return;
+        }
+        var id = 0;
+        this.installedModules = {};
+        Object.keys(items).forEach(function (key) {
+            var item = items[key];
+            var theModule = {
+                module_id: id++,
+                name: key,
+                version: item.version
+            };
+            _this.installedModules[key] = theModule;
+        });
+    };
+    return NodeModuleCollector;
+})();
+exports.NodeModuleCollector = NodeModuleCollector;
 var NodeRequestInfoCollector = (function () {
     function NodeRequestInfoCollector() {
     }
@@ -1383,6 +1484,7 @@ var SIGINT_CODE = 2;
 var defaults = Configuration.defaults;
 defaults.environmentInfoCollector = new NodeEnvironmentInfoCollector();
 defaults.errorParser = new NodeErrorParser();
+defaults.moduleCollector = new NodeModuleCollector();
 defaults.requestInfoCollector = new NodeRequestInfoCollector();
 defaults.submissionAdapter = new NodeSubmissionAdapter();
 function getListenerCount(emitter, event) {
diff --git a/dist/exceptionless.node.js.map b/dist/exceptionless.node.js.map
index f6855f8e..e21f3bcc 100644
--- a/dist/exceptionless.node.js.map
+++ b/dist/exceptionless.node.js.map
@@ -1 +1 @@
-{"version":3,"file":"exceptionless.node.js","sourceRoot":"/source/","sources":["exceptionless.node.ts"],"names":["getListenerCount","onUncaughtException","getExitCodeReason"],"mappings":"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","sourcesContent":["import * as events from \"events\";\nimport * as net from \"net\";\nimport * as stream from \"stream\";\nimport * as child from \"child_process\";\nimport * as tls from \"tls\";\nimport * as http from \"http\";\nimport * as crypto from \"crypto\";\nexport interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority:number = 30;\n  public name:string = 'ErrorPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\nimport os = require('os');\nimport nodestacktrace = require('stack-trace');\nimport https = require('https');\nimport url = require('url');\n                                                                                                                                                                                                                                       \n\nexport class NodeEnvironmentInfoCollector implements IEnvironmentInfoCollector {\n  public getEnvironmentInfo(context:EventPluginContext): IEnvironmentInfo {\n    function getIpAddresses():string {\n      let ips:string[] = [];\n      let interfaces = os.networkInterfaces();\n      Object.keys(interfaces).forEach((name) => {\n        interfaces[name].forEach((iface:any) => {\n          if ('IPv4' === iface.family && !iface.internal) {\n            ips.push(iface.address);\n          }\n        });\n      });\n\n      return ips.join(', ');\n    }\n\n    if (!os) {\n      return null;\n    }\n\n    let environmentInfo: IEnvironmentInfo = {\n      processor_count: os.cpus().length,\n      total_physical_memory: os.totalmem(),\n      available_physical_memory: os.freemem(),\n      command_line: process.argv.join(' '),\n      process_name: (process.title || '').replace(/[\\uE000-\\uF8FF]/g, ''),\n      process_id: process.pid + '',\n      process_memory_size: process.memoryUsage().heapTotal,\n      // thread_id: '',\n      architecture: os.arch(),\n      o_s_name: os.type(),\n      o_s_version: os.release(),\n      ip_address: getIpAddresses(),\n      machine_name: os.hostname(),\n      // install_id: '',\n      runtime_version: process.version,\n      data: {\n        loadavg: os.loadavg(),\n        platform: os.platform(),\n        tmpdir: os.tmpdir(),\n        uptime: os.uptime()\n      }\n    };\n\n    if ((<any>os).endianness) {\n      environmentInfo.data.endianness = (<any>os).endianness();\n    }\n\n    return environmentInfo;\n  }\n}\n\n                                                                                                                                                                                                                                                                   \n\nexport class NodeErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getStackFrames(stackFrames:any[]): IStackFrame[] {\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: frame.getMethodName() || frame.getFunctionName(),\n          // parameters: frame.args,\n          file_name: frame.getFileName(),\n          line_number: frame.getLineNumber() || 0,\n          column: frame.getColumnNumber() || 0,\n          declaring_type: frame.getTypeName(),\n          data: {\n            is_native: frame.isNative() || (!!frame.filename && frame.filename[0] !== '/' && frame.filename[0] !== '.')\n          }\n        });\n      }\n\n      return frames;\n    }\n\n    if (!nodestacktrace) {\n      throw new Error('Unable to load the stack trace library.');\n    }\n\n    let stackFrames = nodestacktrace.parse(exception) || [];\n    return {\n      type: exception.name,\n      message: exception.message,\n      stack_trace: getStackFrames(stackFrames)\n    };\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class NodeRequestInfoCollector implements IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n    if (!context.contextData[REQUEST_KEY]) {\n      return null;\n    }\n\n    let request = context.contextData[REQUEST_KEY];\n    // TODO: include referrer\n    let requestInfo:IRequestInfo = {\n      client_ip_address: request.ip,\n      user_agent: request.headers['user-agent'],\n      is_secure: request.secure,\n      http_method: request.method,\n      host: request.hostname || request.host,\n      path: request.path,\n      post_data: request.body,\n      cookies: Utils.getCookies((request || {}).headers.cookie),\n      query_string: request.params\n    };\n\n    let host = request.headers.host;\n    let port:number = host && parseInt(host.slice(host.indexOf(':') + 1), 10);\n    if (port > 0) {\n      requestInfo.port = port;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                     \n\nexport class NodeSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean) {\n    if (isAppExiting) {\n      this.sendRequestSync(request, callback);\n      return;\n    }\n\n    let parsedHost = url.parse(request.serverUrl);\n\n    let options: https.RequestOptions = {\n      auth: `client:${request.apiKey}`,\n      headers: {},\n      hostname: parsedHost.hostname,\n      method: request.method,\n      port: parsedHost.port && parseInt(parsedHost.port, 10),\n      path: request.path\n    };\n\n    options.headers['User-Agent'] = request.userAgent;\n\n    if (request.method === 'POST') {\n      options.headers = {\n        'Content-Type': 'application/json',\n        'Content-Length': request.data.length\n      };\n    }\n\n    let protocol = (parsedHost.protocol === 'https' ? https : http);\n    let clientRequest: http.ClientRequest = protocol.request(options, (response: http.IncomingMessage) => {\n      let body = '';\n      response.setEncoding('utf8');\n      response.on('data', (chunk) => body += chunk);\n      response.on('end', () => this.complete(response, body, response.headers, callback));\n    });\n\n    clientRequest.on('error', (error: Error) => callback(500, error.message));\n    clientRequest.end(request.data);\n  }\n\n  private complete(response: http.IncomingMessage, responseBody: string, responseHeaders: Object, callback: SubmissionCallback): void {\n    let message: string;\n    if (response.statusCode === 0) {\n    message = 'Unable to connect to server.';\n    } else if (response.statusCode < 200 || response.statusCode > 299) {\n    message = response.statusMessage || (<any>response).message;\n    }\n\n    callback(response.statusCode || 500, message, responseBody, responseHeaders);\n  }\n\n  private sendRequestSync(request: SubmissionRequest, callback: SubmissionCallback): void {\n    let requestJson = JSON.stringify(request);\n    let res = child.spawnSync(process.execPath, [require.resolve('./submitSync.js')],\n      {\n        input: requestJson,\n        stdio: ['pipe', 'pipe', process.stderr]\n      });\n\n    let out = res.stdout.toString();\n    let result = JSON.parse(out);\n\n    callback(result.status, result.message, result.data, result.headers);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    \n\nconst EXIT: string = 'exit';\nconst UNCAUGHT_EXCEPTION: string = 'uncaughtException';\nconst SIGINT: string = 'SIGINT';\nconst SIGINT_CODE: number = 2;\n\nlet defaults = Configuration.defaults;\ndefaults.environmentInfoCollector = new NodeEnvironmentInfoCollector();\ndefaults.errorParser = new NodeErrorParser();\ndefaults.requestInfoCollector = new NodeRequestInfoCollector();\ndefaults.submissionAdapter = new NodeSubmissionAdapter();\n\nfunction getListenerCount(emitter, event:string): number {\n  if (emitter.listenerCount) {\n    return emitter.listenerCount(event);\n  }\n  return require('events').listenerCount(emitter, event);\n}\n\n/*\n * Adding a event handler for 'uncaughtException' modifies the default\n * Node behavior, so it won't exit or log to the console. Instead,\n * we hijack the event emitter and forward the exception to the callback.\n */\nfunction onUncaughtException(callback: (error: Error) => void) {\n  let originalEmit = process.emit;\n\n  process.emit = function(type: string, error: Error) {\n    if (type === UNCAUGHT_EXCEPTION) {\n      callback(error);\n    }\n\n    return originalEmit.apply(this, arguments);\n  };\n}\n\nonUncaughtException(function(error: Error) {\n  ExceptionlessClient.default.submitUnhandledException(error, UNCAUGHT_EXCEPTION);\n});\n\n/*\n * We cannot hijack SIGINT, so if there are no other handlers,\n * we just reproduce default Node.js behavior by exiting.\n */\nprocess.on(SIGINT, function() {\n  if (getListenerCount(process, SIGINT) <= 1) {\n    process.exit(128 + SIGINT_CODE);\n  }\n});\n\nprocess.on(EXIT, function(code: number) {\n  /**\n   * exit codes: https://nodejs.org/api/process.html#process_event_exit\n   * From now on, only synchronous code may run. As soon as this method\n   * ends, the application inevitably will exit.\n   */\n  function getExitCodeReason(exitCode: number): string {\n    if (exitCode === 1) {\n      return 'Uncaught Fatal Exception';\n    }\n\n    if (exitCode === 3) {\n      return 'Internal JavaScript Parse Error';\n    }\n\n    if (exitCode === 4) {\n      return 'Internal JavaScript Evaluation Failure';\n    }\n\n    if (exitCode === 5) {\n      return 'Fatal Exception';\n    }\n\n    if (exitCode === 6) {\n      return 'Non-function Internal Exception Handler ';\n    }\n\n    if (exitCode === 7) {\n      return 'Internal Exception Handler Run-Time Failure';\n    }\n\n    if (exitCode === 8) {\n      return 'Uncaught Exception';\n    }\n\n    if (exitCode === 9) {\n      return 'Invalid Argument';\n    }\n\n    if (exitCode === 10) {\n      return 'Internal JavaScript Run-Time Failure';\n    }\n\n    if (exitCode === 12) {\n      return 'Invalid Debug Argument';\n    }\n\n    return null;\n  }\n\n  let client = ExceptionlessClient.default;\n  let message = getExitCodeReason(code);\n\n  if (message !== null) {\n    client.submitLog(EXIT, message, 'Error');\n  }\n\n  client.config.queue.process(true);\n  // Application will now exit.\n});\n\n(<any>Error).stackTraceLimit = Infinity;\n\n"]}
\ No newline at end of file
+{"version":3,"file":"exceptionless.node.js","sourceRoot":"/source/","sources":["exceptionless.node.ts"],"names":["getListenerCount","onUncaughtException","getExitCodeReason"],"mappings":"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","sourcesContent":["import * as events from \"events\";\nimport * as net from \"net\";\nimport * as stream from \"stream\";\nimport * as child from \"child_process\";\nimport * as tls from \"tls\";\nimport * as http from \"http\";\nimport * as crypto from \"crypto\";\nexport interface IEvent {\n  type?:string;\n  source?:string;\n  date?:Date;\n  tags?:string[];\n  message?:string;\n  geo?:string;\n  value?:number;\n  data?:any;\n  reference_id?:string;\n  session_id?:string;\n}\n\nexport interface ILastReferenceIdManager {\n  getLast(): string;\n  clearLast(): void;\n  setLast(eventId:string): void;\n}\n\nexport interface ILog {\n  info(message:string):void;\n  warn(message:string):void;\n  error(message:string):void;\n}\n\n                                          \n\nexport interface IEventQueue {\n  enqueue(event:IEvent):void;\n  process(isAppExiting?:boolean):void;\n  suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean):void;\n}\n\n                                                                                                                                  \n\nexport interface IEnvironmentInfoCollector {\n  getEnvironmentInfo(context:EventPluginContext):IEnvironmentInfo;\n}\n\n                                                                                                              \n\nexport interface IErrorParser {\n  parse(context:EventPluginContext, exception:Error): IError;\n}\n\n                                                                                                                \n\nexport interface IModuleCollector {\n  getModules(context:EventPluginContext):IModule[];\n}\n\n                                                                                                                          \n\nexport interface IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo;\n}\n\n                                              \n\nexport interface IStorage<T> {\n  save(path:string, value:T):boolean;\n  get(path:string):T;\n  getList(searchPattern?:string, limit?:number):IStorageItem<T>[];\n  remove(path:string):void;\n}\n\n                                                                                                                   \n\nexport interface ISubmissionAdapter {\n  sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean): void;\n}\n\n                                                                                                                                                                                                                                                                                           \n\nexport interface ISubmissionClient {\n  postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void;\n  postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void;\n  getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void;\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            \n\nexport interface IConfigurationSettings {\n  apiKey?:string;\n  serverUrl?:string;\n  environmentInfoCollector?:IEnvironmentInfoCollector;\n  errorParser?:IErrorParser;\n  lastReferenceIdManager?:ILastReferenceIdManager;\n  log?:ILog;\n  moduleCollector?:IModuleCollector;\n  requestInfoCollector?:IRequestInfoCollector;\n  submissionBatchSize?:number;\n  submissionClient?:ISubmissionClient;\n  submissionAdapter?:ISubmissionAdapter;\n  storage?:IStorage<any>;\n  queue?:IEventQueue;\n}\n\n                                                                                                                                                     \n\nexport class SettingsManager {\n  /**\n   * The configuration settings path.\n   * @type {string}\n   * @private\n   */\n  private static _configPath:string = 'ex-server-settings.json';\n\n  /**\n   * A list of handlers that will be fired when the settings change.\n   * @type {Array}\n   * @private\n   */\n  private static _handlers:{ (config:Configuration):void }[] = [];\n\n  public static onChanged(handler:(config:Configuration) => void) {\n    !!handler && this._handlers.push(handler);\n  }\n\n  public static applySavedServerSettings(config:Configuration):void {\n    config.log.info('Applying saved settings.');\n    config.settings = Utils.merge(config.settings, this.getSavedServerSettings(config));\n    this.changed(config);\n  }\n\n  public static checkVersion(version:number, config:Configuration):void {\n    if (version) {\n      let savedConfigVersion = parseInt(<string>config.storage.get(`${this._configPath}-version`), 10);\n      if (isNaN(savedConfigVersion) || version > savedConfigVersion) {\n        config.log.info(`Updating settings from v${(!isNaN(savedConfigVersion) ? savedConfigVersion : 0)} to v${version}`);\n        this.updateSettings(config);\n      }\n    }\n  }\n\n  public static updateSettings(config:Configuration):void {\n    if (!config.isValid) {\n      config.log.error('Unable to update settings: ApiKey is not set.');\n      return;\n    }\n\n    config.submissionClient.getSettings(config, (response:SettingsResponse) => {\n      if (!response || !response.success || !response.settings) {\n        return;\n      }\n\n      config.settings = Utils.merge(config.settings, response.settings);\n\n      // TODO: Store snapshot of settings after reading from config and attributes and use that to revert to defaults.\n      // Remove any existing server settings that are not in the new server settings.\n      let savedServerSettings = SettingsManager.getSavedServerSettings(config);\n      for (let key in savedServerSettings) {\n        if (response.settings[key]) {\n          continue;\n        }\n\n        delete config.settings[key];\n      }\n\n      let path = SettingsManager._configPath; // optimization for minifier.\n      config.storage.save(`${path}-version`, response.settingsVersion);\n      config.storage.save(path, response.settings);\n\n      config.log.info('Updated settings');\n      this.changed(config);\n    });\n  }\n\n  private static changed(config:Configuration) {\n    let handlers = this._handlers; // optimization for minifier.\n    for (let index = 0; index < handlers.length; index++) {\n      handlers[index](config);\n    }\n  }\n\n  private static getSavedServerSettings(config:Configuration):Object {\n    return config.storage.get(this._configPath) || {};\n  }\n}\n\n                                                                    \n\nexport class DefaultLastReferenceIdManager implements ILastReferenceIdManager {\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @type {string}\n   * @private\n   */\n  private _lastReferenceId:string = null;\n\n  /**\n   * Gets the last event's reference id that was submitted to the server.\n   * @returns {string}\n   */\n  getLast(): string {\n    return this._lastReferenceId;\n  }\n\n  /**\n   * Clears the last event's reference id.\n   */\n  clearLast():void {\n    this._lastReferenceId = null;\n  }\n\n  /**\n   * Sets the last event's reference id.\n   * @param eventId\n   */\n  setLast(eventId:string):void {\n    this._lastReferenceId = eventId;\n  }\n}\n\n                              \n\nexport class ConsoleLog implements ILog {\n  public info(message:string):void {\n    this.log('info', message);\n  }\n\n  public warn(message:string):void {\n    this.log('warn', message);\n  }\n\n  public error(message:string):void {\n    this.log('error', message);\n  }\n\n  private log(level:string, message:string) {\n    if (console && console[level]) {\n      console[level](`[${level}] Exceptionless: ${message}`);\n    }\n  }\n}\n\n                              \n\nexport class NullLog implements ILog {\n  public info(message:string):void {}\n  public warn(message:string):void {}\n  public error(message:string):void {}\n}\n\nexport interface IUserInfo {\n  identity?:string;\n  name?:string;\n  data?:any;\n}\n\n                                                                                                         \n\nexport interface IEventPlugin {\n  priority?:number;\n  name?:string;\n  run(context:EventPluginContext, next?:() => void): void;\n}\n\n                                                                                                                                                                                             \n\nexport class EventPluginContext {\n  public cancelled:boolean;\n  public client:ExceptionlessClient;\n  public event:IEvent;\n  public contextData:ContextData;\n\n  constructor(client:ExceptionlessClient, event:IEvent, contextData?:ContextData) {\n    this.client = client;\n    this.event = event;\n    this.contextData = contextData ? contextData : new ContextData();\n  }\n\n  public get log(): ILog {\n    return this.client.config.log;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class EventPluginManager {\n  public static run(context:EventPluginContext, callback:(context?:EventPluginContext) => void): void {\n    let wrap = function (plugin:IEventPlugin, next?:() => void): () => void {\n      return () => {\n        try {\n          if (!context.cancelled) {\n            plugin.run(context, next);\n          }\n        } catch (ex) {\n          context.cancelled = true;\n          context.log.error(`Error running plugin '${plugin.name}': ${ex.message}. Discarding Event.`);\n        }\n\n        if (context.cancelled && !!callback) {\n          callback(context);\n        }\n      };\n    };\n\n    let plugins:IEventPlugin[] = context.client.config.plugins; // optimization for minifier.\n    let wrappedPlugins:{ (): void }[] = [];\n    if (!!callback) {\n      wrappedPlugins[plugins.length] = wrap({ name: 'cb', priority: 9007199254740992, run: callback }, null);\n    }\n\n    for (let index = plugins.length - 1; index > -1; index--) {\n      wrappedPlugins[index] = wrap(plugins[index], !!callback || (index < plugins.length - 1) ? wrappedPlugins[index + 1] : null);\n    }\n\n    wrappedPlugins[0]();\n  }\n\n  public static addDefaultPlugins(config:Configuration): void {\n    config.addPlugin(new ConfigurationDefaultsPlugin());\n    config.addPlugin(new ErrorPlugin());\n    config.addPlugin(new ModuleInfoPlugin());\n    config.addPlugin(new RequestInfoPlugin());\n    config.addPlugin(new EnvironmentInfoPlugin());\n    config.addPlugin(new SubmissionMethodPlugin());\n  }\n}\n\n                                                                                                                                                \n\nexport class ReferenceIdPlugin implements IEventPlugin {\n  public priority:number = 20;\n  public name:string = 'ReferenceIdPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    if ((!context.event.reference_id || context.event.reference_id.length === 0) && context.event.type === 'error') {\n      context.event.reference_id = Utils.guid().replace('-', '').substring(0, 10);\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class DefaultEventQueue implements IEventQueue {\n  /**\n   * The configuration object.\n   * @type {Configuration}\n   * @private\n   */\n  private _config:Configuration;\n\n  /**\n   * Suspends processing until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _suspendProcessingUntil:Date;\n\n  /**\n   * Discards queued items until the specified time.\n   * @type {Date}\n   * @private\n   */\n  private _discardQueuedItemsUntil:Date;\n\n  /**\n   * Returns true if the queue is processing.\n   * @type {boolean}\n   * @private\n   */\n  private _processingQueue:boolean = false;\n\n  /**\n   * Processes the queue every xx seconds.\n   * @type {Timer}\n   * @private\n   */\n  private _queueTimer:any;\n\n  constructor(config:Configuration) {\n    this._config = config;\n  }\n\n  public enqueue(event:IEvent): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n    this.ensureQueueTimer();\n\n    if (this.areQueuedItemsDiscarded()) {\n      config.log.info('Queue items are currently being discarded. The event will not be queued.');\n      return;\n    }\n\n    let key = `ex-q-${new Date().toJSON()}-${Utils.randomNumber()}`;\n    config.log.info(`Enqueuing event: ${key} type=${event.type} ${!!event.reference_id ? 'refid=' + event.reference_id : ''}`);\n    config.storage.save(key, event);\n  }\n\n  public process(isAppExiting?:boolean): void {\n    function getEvents(events:{ path:string, value:IEvent }[]):IEvent[] {\n      let items:IEvent[] = [];\n      for (let index = 0; index < events.length; index++) {\n        items.push(events[index].value);\n      }\n\n      return items;\n    }\n\n    const queueNotProcessed:string = 'The queue will not be processed.'; // optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    this.ensureQueueTimer();\n\n    if (this._processingQueue) {\n      return;\n    }\n\n    log.info('Processing queue...');\n    if (!config.enabled) {\n      log.info(`Configuration is disabled. ${queueNotProcessed}`);\n      return;\n    }\n\n    if (!config.isValid) {\n      log.info(`Invalid Api Key. ${queueNotProcessed}`);\n      return;\n    }\n\n    this._processingQueue = true;\n\n    try {\n      let events = config.storage.getList('ex-q', config.submissionBatchSize);\n      if (!events || events.length === 0) {\n        this._processingQueue = false;\n        return;\n      }\n\n      log.info(`Sending ${events.length} events to ${config.serverUrl}.`);\n      config.submissionClient.postEvents(getEvents(events), config, (response:SubmissionResponse) => {\n        this.processSubmissionResponse(response, events);\n        log.info('Finished processing queue.');\n        this._processingQueue = false;\n      }, isAppExiting);\n    } catch (ex) {\n      log.error(`Error processing queue: ${ex}`);\n      this.suspendProcessing();\n      this._processingQueue = false;\n    }\n  }\n\n  public suspendProcessing(durationInMinutes?:number, discardFutureQueuedItems?:boolean, clearQueue?:boolean): void {\n    let config:Configuration = this._config; // Optimization for minifier.\n\n    if (!durationInMinutes || durationInMinutes <= 0) {\n      durationInMinutes = 5;\n    }\n\n    config.log.info(`Suspending processing for ${durationInMinutes} minutes.`);\n    this._suspendProcessingUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n\n    if (discardFutureQueuedItems) {\n      this._discardQueuedItemsUntil = new Date(new Date().getTime() + (durationInMinutes * 60000));\n    }\n\n    if (clearQueue) {\n      // Account is over the limit and we want to ensure that the sample size being sent in will contain newer errors.\n      this.removeEvents(config.storage.getList('ex-q'));\n    }\n  }\n\n  private areQueuedItemsDiscarded(): boolean {\n    return this._discardQueuedItemsUntil && this._discardQueuedItemsUntil > new Date();\n  }\n\n  private ensureQueueTimer(): void {\n    if (!this._queueTimer) {\n      this._queueTimer = setInterval(() => this.onProcessQueue(), 10000);\n    }\n  }\n\n  private isQueueProcessingSuspended(): boolean {\n    return this._suspendProcessingUntil && this._suspendProcessingUntil > new Date();\n  }\n\n  private onProcessQueue(): void {\n    if (!this.isQueueProcessingSuspended() && !this._processingQueue) {\n      this.process();\n    }\n  }\n\n  private processSubmissionResponse(response:SubmissionResponse, events:{ path:string, value:IEvent }[]): void {\n    const noSubmission:string = 'The event will not be submitted.'; // Optimization for minifier.\n    let config:Configuration = this._config; // Optimization for minifier.\n    let log:ILog = config.log; // Optimization for minifier.\n\n    if (response.success) {\n      log.info(`Sent ${events.length} events.`);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.serviceUnavailable) {\n      // You are currently over your rate limit or the servers are under stress.\n      log.error('Server returned service unavailable.');\n      this.suspendProcessing();\n      return;\n    }\n\n    if (response.paymentRequired) {\n      // If the organization over the rate limit then discard the event.\n      log.info('Too many events have been submitted, please upgrade your plan.');\n      this.suspendProcessing(null, true, true);\n      return;\n    }\n\n    if (response.unableToAuthenticate) {\n      // The api key was suspended or could not be authorized.\n      log.info(`Unable to authenticate, please check your configuration. ${noSubmission}`);\n      this.suspendProcessing(15);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.notFound || response.badRequest) {\n      // The service end point could not be found.\n      log.error(`Error while trying to submit data: ${response.message}`);\n      this.suspendProcessing(60 * 4);\n      this.removeEvents(events);\n      return;\n    }\n\n    if (response.requestEntityTooLarge) {\n      let message = 'Event submission discarded for being too large.';\n      if (config.submissionBatchSize > 1) {\n        log.error(`${message} Retrying with smaller batch size.`);\n        config.submissionBatchSize = Math.max(1, Math.round(config.submissionBatchSize / 1.5));\n      } else {\n        log.error(`${message} ${noSubmission}`);\n        this.removeEvents(events);\n      }\n\n      return;\n    }\n\n    if (!response.success) {\n      log.error(`Error submitting events: ${response.message || 'Please check the network tab for more info.'}`);\n      this.suspendProcessing();\n    }\n  }\n\n  private removeEvents(events:{ path:string, value:IEvent }[]) {\n    for (let index = 0; index < (events || []).length; index++) {\n      this._config.storage.remove(events[index].path);\n    }\n  }\n}\n\n                                                                                     \n\nexport class InMemoryStorage<T> implements IStorage<T> {\n  private _items:IStorageItem<T>[] = [];\n  private _maxItems:number;\n\n  constructor(maxItems?:number) {\n    this._maxItems = maxItems > 0 ? maxItems : 250;\n  }\n\n  public save(path:string, value:T):boolean {\n    if (!path || !value) {\n      return false;\n    }\n\n    this.remove(path);\n    if (this._items.push({ created: new Date().getTime(), path: path, value: value }) > this._maxItems) {\n      this._items.shift();\n    }\n\n    return true;\n  }\n\n  public get(path:string):T {\n    let item:IStorageItem<T> = path ? this.getList(`^${path}$`, 1)[0] : null;\n    return item ? item.value : null;\n  }\n\n  public getList(searchPattern?:string, limit?:number):IStorageItem<T>[] {\n    let items = this._items; // Optimization for minifier\n    if (!searchPattern) {\n      return items.slice(0, limit);\n    }\n\n    let regex = new RegExp(searchPattern);\n    let results:IStorageItem<T>[] = [];\n    for (let index = 0; index < items.length; index++) {\n      if (regex.test(items[index].path)) {\n        results.push(items[index]);\n\n        if (results.length >= limit) {\n          break;\n        }\n      }\n    }\n\n    return results;\n  }\n\n  public remove(path:string):void {\n    if (path) {\n      let item = this.getList(`^${path}$`, 1)[0];\n      if (item) {\n        this._items.splice(this._items.indexOf(item), 1);\n      }\n    }\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\ndeclare var XDomainRequest:{ new (); create(); };\n\nexport class DefaultSubmissionClient implements ISubmissionClient {\n  public configurationVersionHeader:string = 'x-exceptionless-configversion';\n\n  public postEvents(events:IEvent[], config:Configuration, callback:(response:SubmissionResponse) => void, isAppExiting?:boolean):void {\n    let data = Utils.stringify(events, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', '/api/v2/events', data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb, isAppExiting);\n  }\n\n  public postUserDescription(referenceId:string, description:IUserDescription, config:Configuration, callback:(response:SubmissionResponse) => void):void {\n    let path = `/api/v2/events/by-ref/${encodeURIComponent(referenceId)}/user-description`;\n    let data = Utils.stringify(description, config.dataExclusions);\n    let request = this.createRequest(config, 'POST', path, data);\n    let cb = this.createSubmissionCallback(config, callback);\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  public getSettings(config:Configuration, callback:(response:SettingsResponse) => void):void {\n    let request = this.createRequest(config, 'GET', '/api/v2/projects/config');\n    let cb = (status, message, data?, headers?) => {\n      if (status !== 200) {\n        return callback(new SettingsResponse(false, null, -1, null, message));\n      }\n\n      let settings:IClientConfiguration;\n      try {\n        settings = JSON.parse(data);\n      } catch (e) {\n        config.log.error(`Unable to parse settings: '${data}'`);\n      }\n\n      if (!settings || isNaN(settings.version)) {\n        return callback(new SettingsResponse(false, null, -1, null, 'Invalid configuration settings.'));\n      }\n\n      callback(new SettingsResponse(true, settings.settings || {}, settings.version));\n    };\n\n    return config.submissionAdapter.sendRequest(request, cb);\n  }\n\n  private createRequest(config: Configuration, method: string, path: string, data: string = null): SubmissionRequest {\n    return {\n      method,\n      path,\n      data,\n      serverUrl: config.serverUrl,\n      apiKey: config.apiKey,\n      userAgent: config.userAgent\n    };\n  }\n\n  private createSubmissionCallback(config:Configuration, callback:(response:SubmissionResponse) => void) {\n    return (status, message, data?, headers?) => {\n      let settingsVersion:number = headers && parseInt(headers[this.configurationVersionHeader], 10);\n      SettingsManager.checkVersion(settingsVersion, config);\n\n      callback(new SubmissionResponse(status, message));\n    };\n  }\n}\n\nexport class Utils {\n  public static addRange<T>(target:T[], ...values:T[]) {\n    if (!target) {\n      target = [];\n    }\n\n    if (!values || values.length === 0) {\n      return target;\n    }\n\n    for (let index = 0; index < values.length; index++) {\n      if (values[index] && target.indexOf(values[index]) < 0) {\n        target.push(values[index]);\n      }\n    }\n\n    return target;\n  }\n\n  public static getHashCode(source:string): string {\n    if (!source || source.length === 0) {\n      return null;\n    }\n\n    let hash:number = 0;\n    for (let index = 0; index < source.length; index++) {\n      let character   = source.charCodeAt(index);\n      hash  = ((hash << 5) - hash) + character;\n      hash |= 0;\n    }\n\n    return hash.toString();\n  }\n\n  public static getCookies(cookies:string): Object {\n    let result:Object = {};\n\n    let parts:string[] = (cookies || '').split('; ');\n    for (let index = 0; index < parts.length; index++) {\n      let cookie:string[] = parts[index].split('=');\n      result[cookie[0]] = cookie[1];\n    }\n\n    return result;\n  }\n\n  public static guid(): string {\n    function s4() {\n      return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);\n    }\n\n    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();\n  }\n\n  public static merge(defaultValues:Object, values:Object) {\n    let result:Object = {};\n\n    for (let key in defaultValues || {}) {\n      if (!!defaultValues[key]) {\n        result[key] = defaultValues[key];\n      }\n    }\n\n    for (let key in values || {}) {\n      if (!!values[key]) {\n        result[key] = values[key];\n      }\n    }\n\n    return result;\n  }\n\n  public static parseVersion(source:string): string {\n    if (!source) {\n      return null;\n    }\n\n    let versionRegex = /(v?((\\d+)\\.(\\d+)(\\.(\\d+))?)(?:-([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?)/;\n    let matches = versionRegex.exec(source);\n    if (matches && matches.length > 0) {\n      return matches[0];\n    }\n\n    return null;\n  }\n\n  public static parseQueryString(query:string) {\n    if (!query || query.length === 0) {\n      return null;\n    }\n\n    let pairs:string[] = query.split('&');\n    if (pairs.length === 0) {\n      return null;\n    }\n\n    let result:Object = {};\n    for (let index = 0; index < pairs.length; index++) {\n      let pair = pairs[index].split('=');\n      result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n    }\n\n    return result;\n  }\n\n  public static randomNumber(): number {\n    return Math.floor(Math.random() * 9007199254740992);\n  }\n\n  public static stringify(data:any, exclusions?:string[]): string {\n    function checkForMatch(pattern:string, value:string): boolean {\n      if (!pattern || !value || typeof value !== 'string') {\n        return false;\n      }\n\n      let trim = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n      pattern = pattern.toLowerCase().replace(trim, '');\n      value = value.toLowerCase().replace(trim, '');\n\n      if (pattern.length <= 0) {\n        return false;\n      }\n\n      let startsWithWildcard:boolean = pattern[0] === '*';\n      if (startsWithWildcard) {\n        pattern = pattern.slice(1);\n      }\n\n      let endsWithWildcard:boolean = pattern[pattern.length - 1] === '*';\n      if (endsWithWildcard) {\n        pattern = pattern.substring(0, pattern.length - 1);\n      }\n\n      if (startsWithWildcard && endsWithWildcard) {\n        return value.indexOf(pattern) !== -1;\n      }\n\n      if (startsWithWildcard) {\n        return value.lastIndexOf(pattern) === (value.length - pattern.length);\n      }\n\n      if (endsWithWildcard) {\n        return value.indexOf(pattern) === 0;\n      }\n\n      return value === pattern;\n    }\n\n    function stringifyImpl(obj:any, excludedKeys:string[]): string {\n      let cache:string[] = [];\n      return JSON.stringify(obj, function(key:string, value:any) {\n        for (let index = 0; index < (excludedKeys || []).length; index++) {\n          if (checkForMatch(excludedKeys[index], key)) {\n            return;\n          }\n        }\n\n        if (typeof value === 'object' && !!value) {\n          if (cache.indexOf(value) !== -1) {\n            // Circular reference found, discard key\n            return;\n          }\n\n          cache.push(value);\n        }\n\n        return value;\n      });\n    }\n\n    if (({}).toString.call(data) === '[object Array]') {\n      let result = [];\n      for (let index = 0; index < data.length; index++) {\n        result[index] = JSON.parse(stringifyImpl(data[index], exclusions || []));\n      }\n\n      return JSON.stringify(result);\n    }\n\n    return stringifyImpl(data, exclusions || []);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n\nexport class Configuration implements IConfigurationSettings {\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @type {IConfigurationSettings}\n   * @private\n   */\n  private static _defaultSettings:IConfigurationSettings = null;\n\n  /**\n   * A default list of tags that will automatically be added to every\n   * report submitted to the server.\n   *\n   * @type {Array}\n   */\n  public defaultTags:string[] = [];\n\n  /**\n   * A default list of of extended data objects that will automatically\n   * be added to every report submitted to the server.\n   *\n   * @type {{}}\n   */\n  public defaultData:Object = {};\n\n  /**\n   * Whether the client is currently enabled or not. If it is disabled,\n   * submitted errors will be discarded and no data will be sent to the server.\n   *\n   * @returns {boolean}\n   */\n  public enabled:boolean = true;\n\n  public environmentInfoCollector:IEnvironmentInfoCollector;\n  public errorParser:IErrorParser;\n  public lastReferenceIdManager:ILastReferenceIdManager = new DefaultLastReferenceIdManager();\n  public log:ILog;\n  public moduleCollector:IModuleCollector;\n  public requestInfoCollector:IRequestInfoCollector;\n\n  /**\n   * Maximum number of events that should be sent to the server together in a batch. (Defaults to 50)\n   */\n  public submissionBatchSize:number;\n  public submissionAdapter:ISubmissionAdapter;\n  public submissionClient:ISubmissionClient;\n\n  /**\n   * Contains a dictionary of custom settings that can be used to control\n   * the client and will be automatically updated from the server.\n   */\n  public settings:Object = {};\n\n  public storage:IStorage<Object>;\n\n  public queue:IEventQueue;\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @type {Array}\n   * @private\n   */\n  private _plugins:IEventPlugin[] = [];\n\n  constructor(configSettings?:IConfigurationSettings) {\n    function inject(fn:any) {\n      return typeof fn === 'function' ? fn(this) : fn;\n    }\n\n    configSettings = Utils.merge(Configuration.defaults, configSettings);\n\n    this.log = inject(configSettings.log) || new NullLog();\n    this.apiKey = configSettings.apiKey;\n    this.serverUrl = configSettings.serverUrl;\n\n    this.environmentInfoCollector = inject(configSettings.environmentInfoCollector);\n    this.errorParser = inject(configSettings.errorParser);\n    this.lastReferenceIdManager = inject(configSettings.lastReferenceIdManager) || new DefaultLastReferenceIdManager();\n    this.moduleCollector = inject(configSettings.moduleCollector);\n    this.requestInfoCollector = inject(configSettings.requestInfoCollector);\n    this.submissionBatchSize = inject(configSettings.submissionBatchSize) || 50;\n    this.submissionAdapter = inject(configSettings.submissionAdapter);\n    this.submissionClient = inject(configSettings.submissionClient) || new DefaultSubmissionClient();\n    this.storage = inject(configSettings.storage) || new InMemoryStorage<any>();\n    this.queue = inject(configSettings.queue) || new DefaultEventQueue(this);\n\n    SettingsManager.applySavedServerSettings(this);\n    EventPluginManager.addDefaultPlugins(this);\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @type {string}\n   * @private\n   */\n  private _apiKey:string;\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @returns {string}\n   */\n  public get apiKey():string {\n    return this._apiKey;\n  }\n\n  /**\n   * The API key that will be used when sending events to the server.\n   * @param value\n   */\n  public set apiKey(value:string) {\n    this._apiKey = value || null;\n    this.log.info(`apiKey: ${this._apiKey}`);\n  }\n\n  /**\n   * Returns true if the apiKey is valid.\n   * @returns {boolean}\n   */\n  public get isValid():boolean {\n    return !!this.apiKey && this.apiKey.length >= 10;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @type {string}\n   * @private\n   */\n  private _serverUrl:string = 'https://collector.exceptionless.io';\n\n  /**\n   * The server url that all events will be sent to.\n   * @returns {string}\n   */\n  public get serverUrl():string {\n    return this._serverUrl;\n  }\n\n  /**\n   * The server url that all events will be sent to.\n   * @param value\n   */\n  public set serverUrl(value:string) {\n    if (!!value) {\n      this._serverUrl = value;\n      this.log.info(`serverUrl: ${this._serverUrl}`);\n    }\n  }\n\n  /**\n   * A list of exclusion patterns.\n   * @type {Array}\n   * @private\n   */\n  private _dataExclusions:string[] = [];\n\n  /**\n   *  A list of exclusion patterns that will automatically remove any data that\n   *  matches them from any data submitted to the server.\n   *\n   *  For example, entering CreditCard will remove any extended data properties,\n   *  form fields, cookies and query parameters from the report.\n   *\n   * @returns {string[]}\n   */\n  public get dataExclusions():string[] {\n    let exclusions:string = this.settings['@@DataExclusions'];\n    return this._dataExclusions.concat(exclusions && exclusions.split(',') || []);\n  }\n\n  /**\n   * Add items to the list of exclusion patterns that will automatically remove any\n   * data that matches them from any data submitted to the server.\n   *\n   * For example, entering CreditCard will remove any extended data properties, form\n   * fields, cookies and query parameters from the report.\n   *\n   * @param exclusions\n   */\n  public addDataExclusions(...exclusions:string[]) {\n    this._dataExclusions = Utils.addRange<string>(this._dataExclusions, ...exclusions);\n  }\n\n  /**\n   * The list of plugins that will be used in this configuration.\n   * @returns {IEventPlugin[]}\n   */\n  public get plugins():IEventPlugin[] {\n    return this._plugins.sort((p1:IEventPlugin, p2:IEventPlugin) => {\n      return (p1.priority < p2.priority) ? -1 : (p1.priority > p2.priority) ? 1 : 0;\n    });\n  }\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param plugin\n   */\n  public addPlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Register an plugin to be used in this configuration.\n   * @param name The name used to identify the plugin.\n   * @param priority Used to determine plugins priority.\n   * @param pluginAction A function that is run.\n   */\n  public addPlugin(name:string, priority:number, pluginAction:(context:EventPluginContext, next?:() => void) => void): void;\n  public addPlugin(pluginOrName:IEventPlugin|string, priority?:number, pluginAction?:(context:EventPluginContext, next?:() => void) => void): void {\n    let plugin:IEventPlugin = !!pluginAction ? { name: <string>pluginOrName, priority: priority, run: pluginAction } : <IEventPlugin>pluginOrName;\n    if (!plugin || !plugin.run) {\n      this.log.error('Add plugin failed: Run method not defined');\n      return;\n    }\n\n    if (!plugin.name) {\n      plugin.name = Utils.guid();\n    }\n\n    if (!plugin.priority) {\n      plugin.priority = 0;\n    }\n\n    let pluginExists:boolean = false;\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === plugin.name) {\n        pluginExists = true;\n        break;\n      }\n    }\n\n    if (!pluginExists) {\n      plugins.push(plugin);\n    }\n  }\n\n  /**\n   * Remove the plugin from this configuration.\n   * @param plugin\n   */\n  public removePlugin(plugin:IEventPlugin): void;\n\n  /**\n   * Remove an plugin by key from this configuration.\n   * @param name\n   */\n  public removePlugin(name:string): void;\n  public removePlugin(pluginOrName:IEventPlugin|string): void {\n    let name:string = typeof pluginOrName === 'string' ? pluginOrName : pluginOrName.name;\n    if (!name) {\n      this.log.error('Remove plugin failed: Plugin name not defined');\n      return;\n    }\n\n    let plugins = this._plugins; // optimization for minifier.\n    for (let index = 0; index < plugins.length; index++) {\n      if (plugins[index].name === name) {\n        plugins.splice(index, 1);\n        break;\n      }\n    }\n  }\n\n  /**\n   * Automatically set the application version for events.\n   * @param version\n   */\n  public setVersion(version:string): void {\n    if (!!version) {\n      this.defaultData['@version'] = version;\n    }\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): void;\n  public setUserIdentity(identity:string): void;\n  public setUserIdentity(identity:string, name:string): void;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): void {\n    const USER_KEY:string = '@user'; // optimization for minifier.\n    let userInfo:IUserInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n\n    let shouldRemove:boolean = !userInfo || (!userInfo.identity && !userInfo.name);\n    if (shouldRemove) {\n      delete this.defaultData[USER_KEY];\n    } else {\n      this.defaultData[USER_KEY] = userInfo;\n    }\n\n    this.log.info(`user identity: ${shouldRemove ? 'null' : userInfo.identity}`);\n  }\n\n  /**\n   * Used to identify the client that sent the events to the server.\n   * @returns {string}\n   */\n  public get userAgent():string {\n    return 'exceptionless-js/1.0.0.0';\n  }\n\n  /**\n   * Automatically set a reference id for error events.\n   */\n  public useReferenceIds(): void {\n    this.addPlugin(new ReferenceIdPlugin());\n  }\n\n  // TODO: Support a min log level.\n  public useDebugLogger(): void {\n    this.log = new ConsoleLog();\n  }\n\n  /**\n   * The default configuration settings that are applied to new configuration instances.\n   * @returns {IConfigurationSettings}\n   */\n  public static get defaults() {\n    if (Configuration._defaultSettings === null) {\n      Configuration._defaultSettings = {};\n    }\n\n    return Configuration._defaultSettings;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                               \n\nexport class EventBuilder {\n  public target:IEvent;\n  public client:ExceptionlessClient;\n  public pluginContextData:ContextData;\n\n  private _validIdentifierErrorMessage:string = 'must contain between 8 and 100 alphanumeric or \\'-\\' characters.'; // optimization for minifier.\n\n  constructor(event:IEvent, client:ExceptionlessClient, pluginContextData?:ContextData) {\n    this.target = event;\n    this.client = client;\n    this.pluginContextData = pluginContextData || new ContextData();\n  }\n\n  public setType(type:string): EventBuilder {\n    if (!!type) {\n      this.target.type = type;\n    }\n\n    return this;\n  }\n\n  public setSource(source:string): EventBuilder {\n    if (!!source) {\n      this.target.source = source;\n    }\n\n    return this;\n  }\n\n  public setSessionId(sessionId:string): EventBuilder {\n    if (!this.isValidIdentifier(sessionId)) {\n      throw new Error(`SessionId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.session_id = sessionId;\n    return this;\n  }\n\n  public setReferenceId(referenceId:string): EventBuilder {\n    if (!this.isValidIdentifier(referenceId)) {\n      throw new Error(`ReferenceId ${this._validIdentifierErrorMessage}`);\n    }\n\n    this.target.reference_id = referenceId;\n    return this;\n  }\n\n  public setMessage(message:string): EventBuilder {\n    if (!!message) {\n      this.target.message = message;\n    }\n\n    return this;\n  }\n\n  public setGeo(latitude: number, longitude: number): EventBuilder {\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new Error('Must be a valid latitude value between -90.0 and 90.0.');\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new Error('Must be a valid longitude value between -180.0 and 180.0.');\n    }\n\n    this.target.geo = `${latitude},${longitude}`;\n    return this;\n  }\n\n  public setUserIdentity(userInfo:IUserInfo): EventBuilder;\n  public setUserIdentity(identity:string): EventBuilder;\n  public setUserIdentity(identity:string, name:string): EventBuilder;\n  public setUserIdentity(userInfoOrIdentity:IUserInfo|string, name?:string): EventBuilder {\n    let userInfo = typeof userInfoOrIdentity !== 'string' ? userInfoOrIdentity : { identity: userInfoOrIdentity, name: name };\n    if (!userInfo || (!userInfo.identity && !userInfo.name)) {\n      return this;\n    }\n\n    this.setProperty('@user', userInfo);\n    return this;\n  }\n\n  public setValue(value:number): EventBuilder {\n    if (!!value) {\n      this.target.value = value;\n    }\n\n    return this;\n  }\n\n  public addTags(...tags:string[]): EventBuilder {\n    this.target.tags = Utils.addRange<string>(this.target.tags, ...tags);\n    return this;\n  }\n\n  public setProperty(name:string, value:any): EventBuilder {\n    if (!name || (value === undefined || value == null)) {\n      return this;\n    }\n\n    if (!this.target.data) {\n      this.target.data = {};\n    }\n\n    this.target.data[name] = value;\n    return this;\n  }\n\n  public markAsCritical(critical:boolean): EventBuilder {\n    if (critical) {\n      this.addTags('Critical');\n    }\n\n    return this;\n  }\n\n  public addRequestInfo(request:Object): EventBuilder {\n    if (!!request) {\n      this.pluginContextData['@request'] = request;\n    }\n\n    return this;\n  }\n\n  public submit(callback?:(context:EventPluginContext) => void): void {\n    this.client.submitEvent(this.target, this.pluginContextData, callback);\n  }\n\n  private isValidIdentifier(value:string): boolean {\n    if (!value) {\n      return true;\n    }\n\n    if (value.length < 8 || value.length > 100) {\n      return false;\n    }\n\n    for (var index = 0; index < value.length; index++) {\n      let code = value.charCodeAt(index);\n      let isDigit = (code >= 48) && (code <= 57);\n      let isLetter = ((code >= 65) && (code <= 90)) || ((code >= 97) && (code <= 122));\n      let isMinus = code === 45;\n\n      if (!(isDigit || isLetter) && !isMinus) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n}\n\nexport interface IUserDescription {\n  email_address?:string;\n  description?:string;\n  data?:any;\n}\n\nexport class ContextData {\n  public setException(exception:Error): void {\n    if (exception) {\n      this['@@_Exception'] = exception;\n    }\n  }\n\n  public get hasException(): boolean {\n    return !!this['@@_Exception'];\n  }\n\n  public getException(): Error {\n    return this['@@_Exception'] || null;\n  }\n\n  public markAsUnhandledError(): void {\n    this['@@_IsUnhandledError'] = true;\n  }\n\n  public get isUnhandledError(): boolean {\n    return !!this['@@_IsUnhandledError'];\n  }\n\n  public setSubmissionMethod(method:string): void {\n    if (method) {\n      this['@@_SubmissionMethod'] = method;\n    }\n  }\n\n  public getSubmissionMethod(): string {\n    return this['@@_SubmissionMethod'] || null;\n  }\n}\n\nexport class SubmissionResponse {\n  success:boolean = false;\n  badRequest:boolean = false;\n  serviceUnavailable:boolean = false;\n  paymentRequired:boolean = false;\n  unableToAuthenticate:boolean = false;\n  notFound:boolean = false;\n  requestEntityTooLarge:boolean = false;\n  statusCode:number;\n  message:string;\n\n  constructor(statusCode:number, message?:string) {\n    this.statusCode = statusCode;\n    this.message = message;\n\n    this.success = statusCode >= 200 && statusCode <= 299;\n    this.badRequest = statusCode === 400;\n    this.serviceUnavailable = statusCode === 503;\n    this.paymentRequired = statusCode === 402;\n    this.unableToAuthenticate = statusCode === 401 || statusCode === 403;\n    this.notFound = statusCode === 404;\n    this.requestEntityTooLarge = statusCode === 413;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n\nexport class ExceptionlessClient {\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   * @private\n   */\n  private static _instance:ExceptionlessClient = null;\n\n  public config:Configuration;\n\n  constructor();\n  constructor(settings:IConfigurationSettings);\n  constructor(apiKey:string, serverUrl?:string);\n  constructor(settingsOrApiKey?:IConfigurationSettings|string, serverUrl?:string) {\n    if (typeof settingsOrApiKey !== 'object') {\n      this.config = new Configuration(settingsOrApiKey);\n    } else {\n      this.config = new Configuration({ apiKey: <string>settingsOrApiKey, serverUrl: serverUrl });\n    }\n  }\n\n  public createException(exception:Error): EventBuilder {\n    let pluginContextData = new ContextData();\n    pluginContextData.setException(exception);\n    return this.createEvent(pluginContextData).setType('error');\n  }\n\n  public submitException(exception:Error, callback?:(context:EventPluginContext) => void): void {\n    this.createException(exception).submit(callback);\n  }\n\n  public createUnhandledException(exception:Error, submissionMethod?:string): EventBuilder {\n    let builder = this.createException(exception);\n    builder.pluginContextData.markAsUnhandledError();\n    builder.pluginContextData.setSubmissionMethod(submissionMethod);\n\n    return builder;\n  }\n\n  public submitUnhandledException(exception:Error, submissionMethod?:string, callback?:(context:EventPluginContext) => void) {\n    this.createUnhandledException(exception, submissionMethod).submit(callback);\n  }\n\n  public createFeatureUsage(feature:string): EventBuilder {\n    return this.createEvent().setType('usage').setSource(feature);\n  }\n\n  public submitFeatureUsage(feature:string, callback?:(context:EventPluginContext) => void): void {\n    this.createFeatureUsage(feature).submit(callback);\n  }\n\n  public createLog(message:string): EventBuilder;\n  public createLog(source:string, message:string): EventBuilder;\n  public createLog(source:string, message:string, level:string): EventBuilder;\n  public createLog(sourceOrMessage:string, message?:string, level?:string): EventBuilder {\n    let builder = this.createEvent().setType('log');\n\n    if (message && level) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message).setProperty('@level', level);\n    } else if (message) {\n      builder = builder.setSource(sourceOrMessage).setMessage(message);\n    } else {\n      // TODO: Look into using https://www.stevefenton.co.uk/Content/Blog/Date/201304/Blog/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/\n      let caller:any = arguments.callee.caller;\n      builder = builder.setSource(caller && caller.name).setMessage(sourceOrMessage);\n    }\n\n    return builder;\n  }\n\n  public submitLog(message:string): void;\n  public submitLog(source:string, message:string): void;\n  public submitLog(source:string, message:string, level:string, callback?:(context:EventPluginContext) => void): void;\n  public submitLog(sourceOrMessage:string, message?:string, level?:string, callback?:(context:EventPluginContext) => void): void {\n    this.createLog(sourceOrMessage, message, level).submit(callback);\n  }\n\n  public createNotFound(resource:string): EventBuilder {\n    return this.createEvent().setType('404').setSource(resource);\n  }\n\n  public submitNotFound(resource:string, callback?:(context:EventPluginContext) => void): void {\n    this.createNotFound(resource).submit(callback);\n  }\n\n  public createSessionStart(sessionId:string): EventBuilder {\n    return this.createEvent().setType('start').setSessionId(sessionId);\n  }\n\n  public submitSessionStart(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionStart(sessionId).submit(callback);\n  }\n\n  public createSessionEnd(sessionId:string): EventBuilder {\n    return this.createEvent().setType('end').setSessionId(sessionId);\n  }\n\n  public submitSessionEnd(sessionId:string, callback?:(context:EventPluginContext) => void): void {\n    this.createSessionEnd(sessionId).submit(callback);\n  }\n\n  public createEvent(pluginContextData?:ContextData): EventBuilder {\n    return new EventBuilder({ date: new Date() }, this, pluginContextData);\n  }\n\n  /**\n   * Submits the event to be sent to the server.\n   * @param event The event data.\n   * @param pluginContextData Any contextual data objects to be used by Exceptionless plugins to gather default information for inclusion in the report information.\n   * @param callback\n   */\n  public submitEvent(event:IEvent, pluginContextData?:ContextData, callback?:(context:EventPluginContext) => void): void {\n    function cancelled(context:EventPluginContext) {\n      if (!!context) {\n        context.cancelled = true;\n      }\n\n      return !!callback && callback(context);\n    }\n\n    let context = new EventPluginContext(this, event, pluginContextData);\n    if (!event) {\n      return cancelled(context);\n    }\n\n    if (!this.config.enabled) {\n      this.config.log.info('Event submission is currently disabled.');\n      return cancelled(context);\n    }\n\n    if (!event.data) {\n      event.data = {};\n    }\n\n    if (!event.tags || !event.tags.length) {\n      event.tags = [];\n    }\n\n    EventPluginManager.run(context, function (ctx:EventPluginContext) {\n      let ev = ctx.event;\n      if (!ctx.cancelled) {\n        // ensure all required data\n        if (!ev.type || ev.type.length === 0) {\n          ev.type = 'log';\n        }\n\n        if (!ev.date) {\n          ev.date = new Date();\n        }\n\n        let config = ctx.client.config;\n        config.queue.enqueue(ev);\n\n        if (ev.reference_id && ev.reference_id.length > 0) {\n          ctx.log.info(`Setting last reference id '${ev.reference_id}'`);\n          config.lastReferenceIdManager.setLast(ev.reference_id);\n        }\n      }\n\n      !!callback && callback(ctx);\n    });\n  }\n\n  /**\n   * Updates the user's email address and description of an event for the specified reference id.\n   * @param referenceId The reference id of the event to update.\n   * @param email The user's email address to set on the event.\n   * @param description The user's description of the event.\n   */\n  public updateUserEmailAndDescription(referenceId:string, email:string, description:string, callback?:(response:SubmissionResponse) => void) {\n    if (!referenceId || !email || !description || !this.config.enabled) {\n      return !!callback && callback(new SubmissionResponse(500, 'cancelled'));\n    }\n\n    let userDescription:IUserDescription = { email_address: email, description: description };\n    this.config.submissionClient.postUserDescription(referenceId, userDescription, this.config, (response:SubmissionResponse) => {\n      if (!response.success) {\n        this.config.log.error(`Failed to submit user email and description for event '${referenceId}': ${response.statusCode} ${response.message}`);\n      }\n\n      !!callback && callback(response);\n    });\n  }\n\n  /**\n   * Gets the last event client id that was submitted to the server.\n   * @returns {string} The event client id.\n   */\n  public getLastReferenceId(): string {\n    return this.config.lastReferenceIdManager.getLast();\n  }\n\n  /**\n   * The default ExceptionlessClient instance.\n   * @type {ExceptionlessClient}\n   */\n  public static get default() {\n    if (ExceptionlessClient._instance === null) {\n      ExceptionlessClient._instance = new ExceptionlessClient(null);\n    }\n\n    return ExceptionlessClient._instance;\n  }\n}\n\nexport interface IModule {\n  data?:any;\n\n  module_id?:number;\n  name?:string;\n  version?:string;\n  is_entry?:boolean;\n  created_date?:Date;\n  modified_date?:Date;\n}\n\nexport interface IRequestInfo {\n  user_agent?:string;\n  http_method?:string;\n  is_secure?:boolean;\n  host?:string;\n  port?:number;\n  path?:string;\n  referrer?:string;\n  client_ip_address?:string;\n  cookies?:any;\n  post_data?:any;\n  query_string?:any;\n  data?:any;\n}\n\nexport interface IEnvironmentInfo {\n  processor_count?:number;\n  total_physical_memory?:number;\n  available_physical_memory?:number;\n  command_line?:string;\n  process_name?:string;\n  process_id?:string;\n  process_memory_size?:number;\n  thread_id?:string;\n  architecture?:string;\n  o_s_name?:string;\n  o_s_version?:string;\n  ip_address?:string;\n  machine_name?:string;\n  install_id?:string;\n  runtime_version?:string;\n  data?:any;\n}\n\n                                                                                                           \n\nexport class ConfigurationDefaultsPlugin implements IEventPlugin {\n  public priority:number = 10;\n  public name:string = 'ConfigurationDefaultsPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let defaultTags:string[] = context.client.config.defaultTags || [];\n    for (let index = 0; index < defaultTags.length; index++) {\n      let tag = defaultTags[index];\n      if (!!tag && context.event.tags.indexOf(tag) < 0) {\n        context.event.tags.push(tag);\n      }\n    }\n\n    let defaultData:Object = context.client.config.defaultData || {};\n    for (let key in defaultData) {\n      if (!!defaultData[key]) {\n        context.event.data[key] = defaultData[key];\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class ErrorPlugin implements IEventPlugin {\n  public priority: number = 30;\n  public name: string = 'ErrorPlugin';\n  public ignoredProperties: string[] = [\n    'arguments',\n    'column',\n    'columnNumber',\n    'description',\n    'fileName',\n    'message',\n    'name',\n    'number',\n    'line',\n    'lineNumber',\n    'opera#sourceloc',\n    'sourceURL',\n    'stack',\n    'stacktrace'\n  ];\n\n  public run(context: EventPluginContext, next?: () => void): void {\n    const ERROR_KEY: string = '@error'; // optimization for minifier.\n    const EXTRA_PROPERTIES_KEY: string = '@ext';\n\n    let exception = context.contextData.getException();\n    if (!!exception) {\n      context.event.type = 'error';\n\n      if (!context.event.data[ERROR_KEY]) {\n        let parser = context.client.config.errorParser;\n        if (!parser) {\n          throw new Error('No error parser was defined.');\n        }\n\n        let result = parser.parse(context, exception);\n        if (!!result) {\n          let additionalData = this.getAdditionalData(exception);\n          if (!!additionalData) {\n            if (!result.data) {\n              result.data = {};\n            }\n            result.data[EXTRA_PROPERTIES_KEY] = additionalData;\n          }\n\n          context.event.data[ERROR_KEY] = result;\n        }\n      }\n    }\n\n    next && next();\n  }\n\n  private getAdditionalData(exception: Error): { [key: string]: any } {\n    let keys = Object.keys(exception)\n      .filter(key => this.ignoredProperties.indexOf(key) < 0);\n\n    if (keys.length === 0) {\n      return null;\n    }\n\n    let additionalData = {};\n\n    keys.forEach(key => {\n      let value = exception[key];\n      if (typeof value !== 'function') {\n        additionalData[key] = value;\n      }\n    });\n\n    return additionalData;\n  }\n}\n\n                                                                                                                                                           \n\nexport class ModuleInfoPlugin implements IEventPlugin {\n  public priority:number = 40;\n  public name:string = 'ModuleInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ERROR_KEY:string = '@error'; // optimization for minifier.\n\n    let collector = context.client.config.moduleCollector;\n    if (context.event.data[ERROR_KEY] && !context.event.data['@error'].modules && !!collector) {\n      let modules:IModule[] = collector.getModules(context);\n      if (modules && modules.length > 0) {\n        context.event.data[ERROR_KEY].modules = modules;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                     \n\nexport class RequestInfoPlugin implements IEventPlugin {\n  public priority:number = 60;\n  public name:string = 'RequestInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n\n    let collector = context.client.config.requestInfoCollector;\n    if (!context.event.data[REQUEST_KEY] && !!collector) {\n      let requestInfo:IRequestInfo = collector.getRequestInfo(context);\n      if (!!requestInfo) {\n        context.event.data[REQUEST_KEY] = requestInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                                                                                             \n\nexport class EnvironmentInfoPlugin implements IEventPlugin {\n  public priority:number = 70;\n  public name:string = 'EnvironmentInfoPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    const ENVIRONMENT_KEY:string = '@environment'; // optimization for minifier.\n\n    let collector = context.client.config.environmentInfoCollector;\n    if (!context.event.data[ENVIRONMENT_KEY] && collector) {\n      let environmentInfo:IEnvironmentInfo = collector.getEnvironmentInfo(context);\n      if (!!environmentInfo) {\n        context.event.data[ENVIRONMENT_KEY] = environmentInfo;\n      }\n    }\n\n    next && next();\n  }\n}\n\n                                                                                                           \n\nexport class SubmissionMethodPlugin implements IEventPlugin {\n  public priority:number = 100;\n  public name:string = 'SubmissionMethodPlugin';\n\n  public run(context:EventPluginContext, next?:() => void): void {\n    let submissionMethod:string = context.contextData.getSubmissionMethod();\n    if (!!submissionMethod) {\n      context.event.data['@submission_method'] = submissionMethod;\n    }\n\n    next && next();\n  }\n}\n\nexport interface IParameter {\n  data?:any;\n  generic_arguments?:string[];\n\n  name?:string;\n  type?:string;\n  type_namespace?:string;\n}\n\n                                          \n\nexport interface IMethod {\n  data?:any;\n  generic_arguments?:string[];\n  parameters?:IParameter[];\n\n  is_signature_target?:boolean;\n  declaring_namespace?:string;\n  declaring_type?:string;\n  name?:string;\n  module_id?:number;\n}\n\n                                    \n\nexport interface IStackFrame extends IMethod {\n  file_name?:string;\n  line_number?:number;\n  column?:number;\n}\n\n                                                                                 \n\nexport interface IInnerError {\n  message?:string;\n  type?:string;\n  code?:string;\n  data?:any;\n  inner?:IInnerError;\n  stack_trace?:IStackFrame[];\n  target_method?:IMethod;\n}\n\n                                                                                 \n\nexport interface IError extends IInnerError {\n  modules?:IModule[];\n}\n\nexport interface IStorageItem<T> {\n  created:number;\n  path:string;\n  value:T;\n}\n\nexport interface SubmissionCallback {\n  (status: number, message: string, data?: string, headers?: Object): void;\n}\n\nexport interface SubmissionRequest {\n  serverUrl: string;\n  apiKey: string;\n  userAgent: string;\n  method: string;\n  path: string;\n  data: string;\n}\n\nexport class SettingsResponse {\n  success:boolean = false;\n  settings:any;\n  settingsVersion:number = -1;\n  message:string;\n  exception:any;\n\n  constructor(success:boolean, settings:any, settingsVersion:number = -1, exception:any = null, message:string = null) {\n    this.success = success;\n    this.settings = settings;\n    this.settingsVersion = settingsVersion;\n    this.exception = exception;\n    this.message = message;\n  }\n}\n\nexport interface IClientConfiguration {\n  settings:Object;\n  version:number;\n}\n\nimport os = require('os');\nimport nodestacktrace = require('stack-trace');\nimport path = require('path');\nimport https = require('https');\nimport url = require('url');\n                                                                                                                                                                                                                                       \n\nexport class NodeEnvironmentInfoCollector implements IEnvironmentInfoCollector {\n  public getEnvironmentInfo(context:EventPluginContext): IEnvironmentInfo {\n    function getIpAddresses():string {\n      let ips:string[] = [];\n      let interfaces = os.networkInterfaces();\n      Object.keys(interfaces).forEach((name) => {\n        interfaces[name].forEach((iface:any) => {\n          if ('IPv4' === iface.family && !iface.internal) {\n            ips.push(iface.address);\n          }\n        });\n      });\n\n      return ips.join(', ');\n    }\n\n    if (!os) {\n      return null;\n    }\n\n    let environmentInfo: IEnvironmentInfo = {\n      processor_count: os.cpus().length,\n      total_physical_memory: os.totalmem(),\n      available_physical_memory: os.freemem(),\n      command_line: process.argv.join(' '),\n      process_name: (process.title || '').replace(/[\\uE000-\\uF8FF]/g, ''),\n      process_id: process.pid + '',\n      process_memory_size: process.memoryUsage().heapTotal,\n      // thread_id: '',\n      architecture: os.arch(),\n      o_s_name: os.type(),\n      o_s_version: os.release(),\n      ip_address: getIpAddresses(),\n      machine_name: os.hostname(),\n      // install_id: '',\n      runtime_version: process.version,\n      data: {\n        loadavg: os.loadavg(),\n        platform: os.platform(),\n        tmpdir: os.tmpdir(),\n        uptime: os.uptime()\n      }\n    };\n\n    if ((<any>os).endianness) {\n      environmentInfo.data.endianness = (<any>os).endianness();\n    }\n\n    return environmentInfo;\n  }\n}\n\n                                                                                                                                                                                                                                                                   \n\nexport class NodeErrorParser implements IErrorParser {\n  public parse(context:EventPluginContext, exception:Error): IError {\n    function getStackFrames(stackFrames:any[]): IStackFrame[] {\n      let frames:IStackFrame[] = [];\n\n      for (let index = 0; index < stackFrames.length; index++) {\n        let frame = stackFrames[index];\n        frames.push({\n          name: frame.getMethodName() || frame.getFunctionName(),\n          // parameters: frame.args,\n          file_name: frame.getFileName(),\n          line_number: frame.getLineNumber() || 0,\n          column: frame.getColumnNumber() || 0,\n          declaring_type: frame.getTypeName(),\n          data: {\n            is_native: frame.isNative() || (!!frame.filename && frame.filename[0] !== '/' && frame.filename[0] !== '.')\n          }\n        });\n      }\n\n      return frames;\n    }\n\n    if (!nodestacktrace) {\n      throw new Error('Unable to load the stack trace library.');\n    }\n\n    let stackFrames = nodestacktrace.parse(exception) || [];\n    return {\n      type: exception.name,\n      message: exception.message,\n      stack_trace: getStackFrames(stackFrames)\n    };\n  }\n}\n\n                                                                                                                                                                                                                                                                                  \n\nexport class NodeModuleCollector implements IModuleCollector {\n\n  private initialized:boolean = false;\n  private installedModules:{[id:string]: IModule} = {};\n\n  public getModules(context:EventPluginContext): IModule[] {\n    this.initialize();\n\n    if (!require.main) {\n      return [];\n    }\n\n    let modulePath = path.dirname(require.main.filename) + '/node_modules/';\n    let pathLength = modulePath.length;\n\n    let loadedKeys = Object.keys(require.cache);\n    let loadedModules = {};\n\n    loadedKeys.forEach(key => {\n      let id = key.substr(pathLength);\n      id = id.substr(0, id.indexOf('/'));\n      loadedModules[id] = true;\n    });\n\n    return Object.keys(loadedModules)\n      .map(key => this.installedModules[key])\n      .filter(m => m !== undefined);\n  }\n\n  private initialize() {\n    if (this.initialized) {\n      return;\n    }\n\n    this.initialized = true;\n\n    let output = child.spawnSync('npm', ['ls', '--depth=0', '--json']).stdout;\n\n    if (!output) {\n      return;\n    }\n\n    let json;\n    try {\n     json = JSON.parse(output.toString());\n    } catch (e) {\n      return;\n    }\n\n    let items = json.dependencies;\n    if (!items) {\n      return;\n    }\n\n    let id = 0;\n    this.installedModules = {};\n\n    Object.keys(items).forEach(key => {\n      let item = items[key];\n      let theModule = <IModule> {\n        module_id: id++,\n        name: key,\n        version: item.version\n      };\n\n      this.installedModules[key] = theModule;\n    });\n  }\n}\n\n                                                                                                                                                                                                                             \n\nexport class NodeRequestInfoCollector implements IRequestInfoCollector {\n  getRequestInfo(context:EventPluginContext):IRequestInfo {\n    const REQUEST_KEY:string = '@request'; // optimization for minifier.\n    if (!context.contextData[REQUEST_KEY]) {\n      return null;\n    }\n\n    let request = context.contextData[REQUEST_KEY];\n    // TODO: include referrer\n    let requestInfo:IRequestInfo = {\n      client_ip_address: request.ip,\n      user_agent: request.headers['user-agent'],\n      is_secure: request.secure,\n      http_method: request.method,\n      host: request.hostname || request.host,\n      path: request.path,\n      post_data: request.body,\n      cookies: Utils.getCookies((request || {}).headers.cookie),\n      query_string: request.params\n    };\n\n    let host = request.headers.host;\n    let port:number = host && parseInt(host.slice(host.indexOf(':') + 1), 10);\n    if (port > 0) {\n      requestInfo.port = port;\n    }\n\n    return requestInfo;\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                     \n\nexport class NodeSubmissionAdapter implements ISubmissionAdapter {\n  public sendRequest(request:SubmissionRequest, callback:SubmissionCallback, isAppExiting?:boolean) {\n    if (isAppExiting) {\n      this.sendRequestSync(request, callback);\n      return;\n    }\n\n    let parsedHost = url.parse(request.serverUrl);\n\n    let options: https.RequestOptions = {\n      auth: `client:${request.apiKey}`,\n      headers: {},\n      hostname: parsedHost.hostname,\n      method: request.method,\n      port: parsedHost.port && parseInt(parsedHost.port, 10),\n      path: request.path\n    };\n\n    options.headers['User-Agent'] = request.userAgent;\n\n    if (request.method === 'POST') {\n      options.headers = {\n        'Content-Type': 'application/json',\n        'Content-Length': request.data.length\n      };\n    }\n\n    let protocol = (parsedHost.protocol === 'https' ? https : http);\n    let clientRequest: http.ClientRequest = protocol.request(options, (response: http.IncomingMessage) => {\n      let body = '';\n      response.setEncoding('utf8');\n      response.on('data', (chunk) => body += chunk);\n      response.on('end', () => this.complete(response, body, response.headers, callback));\n    });\n\n    clientRequest.on('error', (error: Error) => callback(500, error.message));\n    clientRequest.end(request.data);\n  }\n\n  private complete(response: http.IncomingMessage, responseBody: string, responseHeaders: Object, callback: SubmissionCallback): void {\n    let message: string;\n    if (response.statusCode === 0) {\n    message = 'Unable to connect to server.';\n    } else if (response.statusCode < 200 || response.statusCode > 299) {\n    message = response.statusMessage || (<any>response).message;\n    }\n\n    callback(response.statusCode || 500, message, responseBody, responseHeaders);\n  }\n\n  private sendRequestSync(request: SubmissionRequest, callback: SubmissionCallback): void {\n    let requestJson = JSON.stringify(request);\n    let res = child.spawnSync(process.execPath, [require.resolve('./submitSync.js')],\n      {\n        input: requestJson,\n        stdio: ['pipe', 'pipe', process.stderr]\n      });\n\n    let out = res.stdout.toString();\n    let result = JSON.parse(out);\n\n    callback(result.status, result.message, result.data, result.headers);\n  }\n}\n\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n\nconst EXIT: string = 'exit';\nconst UNCAUGHT_EXCEPTION: string = 'uncaughtException';\nconst SIGINT: string = 'SIGINT';\nconst SIGINT_CODE: number = 2;\n\nlet defaults = Configuration.defaults;\ndefaults.environmentInfoCollector = new NodeEnvironmentInfoCollector();\ndefaults.errorParser = new NodeErrorParser();\ndefaults.moduleCollector = new NodeModuleCollector();\ndefaults.requestInfoCollector = new NodeRequestInfoCollector();\ndefaults.submissionAdapter = new NodeSubmissionAdapter();\n\nfunction getListenerCount(emitter, event:string): number {\n  if (emitter.listenerCount) {\n    return emitter.listenerCount(event);\n  }\n  return require('events').listenerCount(emitter, event);\n}\n\n/*\n * Adding a event handler for 'uncaughtException' modifies the default\n * Node behavior, so it won't exit or log to the console. Instead,\n * we hijack the event emitter and forward the exception to the callback.\n */\nfunction onUncaughtException(callback: (error: Error) => void) {\n  let originalEmit = process.emit;\n\n  process.emit = function(type: string, error: Error) {\n    if (type === UNCAUGHT_EXCEPTION) {\n      callback(error);\n    }\n\n    return originalEmit.apply(this, arguments);\n  };\n}\n\nonUncaughtException(function(error: Error) {\n  ExceptionlessClient.default.submitUnhandledException(error, UNCAUGHT_EXCEPTION);\n});\n\n/*\n * We cannot hijack SIGINT, so if there are no other handlers,\n * we just reproduce default Node.js behavior by exiting.\n */\nprocess.on(SIGINT, function() {\n  if (getListenerCount(process, SIGINT) <= 1) {\n    process.exit(128 + SIGINT_CODE);\n  }\n});\n\nprocess.on(EXIT, function(code: number) {\n  /**\n   * exit codes: https://nodejs.org/api/process.html#process_event_exit\n   * From now on, only synchronous code may run. As soon as this method\n   * ends, the application inevitably will exit.\n   */\n  function getExitCodeReason(exitCode: number): string {\n    if (exitCode === 1) {\n      return 'Uncaught Fatal Exception';\n    }\n\n    if (exitCode === 3) {\n      return 'Internal JavaScript Parse Error';\n    }\n\n    if (exitCode === 4) {\n      return 'Internal JavaScript Evaluation Failure';\n    }\n\n    if (exitCode === 5) {\n      return 'Fatal Exception';\n    }\n\n    if (exitCode === 6) {\n      return 'Non-function Internal Exception Handler ';\n    }\n\n    if (exitCode === 7) {\n      return 'Internal Exception Handler Run-Time Failure';\n    }\n\n    if (exitCode === 8) {\n      return 'Uncaught Exception';\n    }\n\n    if (exitCode === 9) {\n      return 'Invalid Argument';\n    }\n\n    if (exitCode === 10) {\n      return 'Internal JavaScript Run-Time Failure';\n    }\n\n    if (exitCode === 12) {\n      return 'Invalid Debug Argument';\n    }\n\n    return null;\n  }\n\n  let client = ExceptionlessClient.default;\n  let message = getExitCodeReason(code);\n\n  if (message !== null) {\n    client.submitLog(EXIT, message, 'Error');\n  }\n\n  client.config.queue.process(true);\n  // Application will now exit.\n});\n\n(<any>Error).stackTraceLimit = Infinity;\n\n"]}
\ No newline at end of file
diff --git a/package.json b/package.json
index d09b025b..819b3ca3 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "phantomjs": "^1.9.18",
     "rimraf": "2.4.3",
     "tracekit": "0.3.0",
-    "tsproject": "1.0.1",
+    "tsproject": "1.0.4",
     "typescript": "1.6.2"
   },
   "dependencies": {
diff --git a/src/exceptionless.node.ts b/src/exceptionless.node.ts
index 0edb4f3e..16fd012f 100644
--- a/src/exceptionless.node.ts
+++ b/src/exceptionless.node.ts
@@ -37,6 +37,7 @@ import { IModuleCollector } from './services/IModuleCollector';
 import { IRequestInfoCollector } from './services/IRequestInfoCollector';
 import { NodeEnvironmentInfoCollector } from './services/NodeEnvironmentInfoCollector';
 import { NodeErrorParser } from './services/NodeErrorParser';
+import { NodeModuleCollector } from './services/NodeModuleCollector';
 import { NodeRequestInfoCollector } from './services/NodeRequestInfoCollector';
 import { InMemoryStorage } from './storage/InMemoryStorage';
 import { IStorage } from './storage/IStorage';
@@ -56,6 +57,7 @@ const SIGINT_CODE: number = 2;
 let defaults = Configuration.defaults;
 defaults.environmentInfoCollector = new NodeEnvironmentInfoCollector();
 defaults.errorParser = new NodeErrorParser();
+defaults.moduleCollector = new NodeModuleCollector();
 defaults.requestInfoCollector = new NodeRequestInfoCollector();
 defaults.submissionAdapter = new NodeSubmissionAdapter();
 
diff --git a/src/plugins/default/ErrorPlugin-spec-exceptions.ts b/src/plugins/default/ErrorPlugin-spec-exceptions.ts
new file mode 100644
index 00000000..9ea52146
--- /dev/null
+++ b/src/plugins/default/ErrorPlugin-spec-exceptions.ts
@@ -0,0 +1,301 @@
+export let CapturedExceptions:any = {};
+
+CapturedExceptions.OPERA_854 = {
+    message: 'Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n' +
+    'Backtrace:\n' +
+    '  Line 44 of linked script http://path/to/file.js\n' +
+    '    this.undef();\n' +
+    '  Line 31 of linked script http://path/to/file.js\n' +
+    '    ex = ex || this.createException();\n' +
+    '  Line 18 of linked script http://path/to/file.js\n' +
+    '    CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    '  Line 4 of inline#1 script in http://path/to/file.js\n' +
+    '    printTrace(printStackTrace());\n' +
+    '  Line 7 of inline#1 script in http://path/to/file.js\n' +
+    '    bar(n - 1);\n' +
+    '  Line 11 of inline#1 script in http://path/to/file.js\n' +
+    '    bar(2);\n' +
+    '  Line 15 of inline#1 script in http://path/to/file.js\n' +
+    '    foo();\n' +
+    '',
+    'opera#sourceloc': 44
+};
+
+CapturedExceptions.OPERA_902 = {
+    message: 'Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n' +
+    'Backtrace:\n' +
+    '  Line 44 of linked script http://path/to/file.js\n' +
+    '    this.undef();\n' +
+    '  Line 31 of linked script http://path/to/file.js\n' +
+    '    ex = ex || this.createException();\n' +
+    '  Line 18 of linked script http://path/to/file.js\n' +
+    '    CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    '  Line 4 of inline#1 script in http://path/to/file.js\n' +
+    '    printTrace(printStackTrace());\n' +
+    '  Line 7 of inline#1 script in http://path/to/file.js\n' +
+    '    bar(n - 1);\n' +
+    '  Line 11 of inline#1 script in http://path/to/file.js\n' +
+    '    bar(2);\n' +
+    '  Line 15 of inline#1 script in http://path/to/file.js\n' +
+    '    foo();\n' +
+    '',
+    'opera#sourceloc': 44
+};
+
+CapturedExceptions.OPERA_927 = {
+    message: 'Statement on line 43: Type mismatch (usually a non-object value used where an object is required)\n' +
+    'Backtrace:\n' +
+    '  Line 43 of linked script http://path/to/file.js\n' +
+    '    bar(n - 1);\n' +
+    '  Line 31 of linked script http://path/to/file.js\n' +
+    '    bar(2);\n' +
+    '  Line 18 of linked script http://path/to/file.js\n' +
+    '    foo();\n' +
+    '',
+    'opera#sourceloc': 43
+};
+
+CapturedExceptions.OPERA_964 = {
+    message: 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)\n' +
+    'Backtrace:\n' +
+    '  Line 42 of linked script http://path/to/file.js\n' +
+    '                this.undef();\n' +
+    '  Line 27 of linked script http://path/to/file.js\n' +
+    '            ex = ex || this.createException();\n' +
+    '  Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' +
+    '        CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    '  Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '             printTrace(printStackTrace());\n' +
+    '  Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '           bar(n - 1);\n' +
+    '  Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' +
+    '           bar(2);\n' +
+    '  Line 15 of inline#1 script in http://path/to/file.js\n' +
+    '         foo();\n' +
+    '',
+    'opera#sourceloc': 42,
+    stacktrace: '  ...  Line 27 of linked script http://path/to/file.js\n' +
+    '            ex = ex || this.createException();\n' +
+    '  Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' +
+    '        CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    '  Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '             printTrace(printStackTrace());\n' +
+    '  Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '           bar(n - 1);\n' +
+    '  Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' +
+    '           bar(2);\n' +
+    '  Line 15 of inline#1 script in http://path/to/file.js\n' +
+    '         foo();\n' +
+    ''
+};
+
+CapturedExceptions.OPERA_10 = {
+    message: 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)',
+    'opera#sourceloc': 42,
+    stacktrace: '  Line 42 of linked script http://path/to/file.js\n' +
+    '                this.undef();\n' +
+    '  Line 27 of linked script http://path/to/file.js\n' +
+    '            ex = ex || this.createException();\n' +
+    '  Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' +
+    '        CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    '  Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '             printTrace(printStackTrace());\n' +
+    '  Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' +
+    '           bar(n - 1);\n' +
+    '  Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' +
+    '           bar(2);\n' +
+    '  Line 15 of inline#1 script in http://path/to/file.js\n' +
+    '         foo();\n' +
+    ''
+};
+
+CapturedExceptions.OPERA_11 = {
+    message: '\'this.undef\' is not a function',
+    stack: '<anonymous function: run>([arguments not available])@http://path/to/file.js:27\n' +
+    'bar([arguments not available])@http://domain.com:1234/path/to/file.js:18\n' +
+    'foo([arguments not available])@http://domain.com:1234/path/to/file.js:11\n' +
+    '<anonymous function>@http://path/to/file.js:15\n' +
+    'Error created at <anonymous function>@http://path/to/file.js:15',
+    stacktrace: 'Error thrown at line 42, column 12 in <anonymous function: createException>() in http://path/to/file.js:\n' +
+    '    this.undef();\n' +
+    'called from line 27, column 8 in <anonymous function: run>(ex) in http://path/to/file.js:\n' +
+    '    ex = ex || this.createException();\n' +
+    'called from line 18, column 4 in printStackTrace(options) in http://path/to/file.js:\n' +
+    '    CapturedExceptions.p = new printStackTrace.implementation(), result = p.run(ex);\n' +
+    'called from line 4, column 5 in bar(n) in http://path/to/file.js:\n' +
+    '    printTrace(printStackTrace());\n' +
+    'called from line 7, column 4 in bar(n) in http://path/to/file.js:\n' +
+    '    bar(n - 1);\n' +
+    'called from line 11, column 4 in foo() in http://path/to/file.js:\n' +
+    '    bar(2);\n' +
+    'called from line 15, column 3 in http://path/to/file.js:\n' +
+    '    foo();'
+};
+
+CapturedExceptions.OPERA_12 = {
+    message: 'Cannot convert \'x\' to object',
+    stack: '<anonymous function>([arguments not available])@http://localhost:8000/ExceptionLab.html:48\n' +
+    'dumpException3([arguments not available])@http://localhost:8000/ExceptionLab.html:46\n' +
+    '<anonymous function>([arguments not available])@http://localhost:8000/ExceptionLab.html:1',
+    stacktrace: 'Error thrown at line 48, column 12 in <anonymous function>(x) in http://localhost:8000/ExceptionLab.html:\n' +
+    '    x.undef();\n' +
+    'called from line 46, column 8 in dumpException3() in http://localhost:8000/ExceptionLab.html:\n' +
+    '    dumpException((function(x) {\n' +
+    'called from line 1, column 0 in <anonymous function>(event) in http://localhost:8000/ExceptionLab.html:\n' +
+    '    dumpException3();'
+};
+
+CapturedExceptions.OPERA_25 = {
+    message: 'Cannot read property \'undef\' of null',
+    name: 'TypeError',
+    stack: 'TypeError: Cannot read property \'undef\' of null\n' +
+    '    at http://path/to/file.js:47:22\n' +
+    '    at foo (http://path/to/file.js:52:15)\n' +
+    '    at bar (http://path/to/file.js:108:168)'
+};
+
+CapturedExceptions.CHROME_15 = {
+    'arguments': ['undef'],
+    message: 'Object #<Object> has no method \'undef\'',
+    stack: 'TypeError: Object #<Object> has no method \'undef\'\n' +
+    '    at bar (http://path/to/file.js:13:17)\n' +
+    '    at bar (http://path/to/file.js:16:5)\n' +
+    '    at foo (http://path/to/file.js:20:5)\n' +
+    '    at http://path/to/file.js:24:4'
+};
+
+CapturedExceptions.CHROME_36 = {
+    message: 'Default error',
+    name: 'Error',
+    stack: 'Error: Default error\n' +
+    '    at dumpExceptionError (http://localhost:8080/file.js:41:27)\n' +
+    '    at HTMLButtonElement.onclick (http://localhost:8080/file.js:107:146)'
+};
+
+CapturedExceptions.FIREFOX_3 = {
+    fileName: 'http://127.0.0.1:8000/js/stacktrace.js',
+    lineNumber: 44,
+    message: 'this.undef is not a function',
+    name: 'TypeError',
+    stack: '()@http://127.0.0.1:8000/js/stacktrace.js:44\n' +
+    '(null)@http://127.0.0.1:8000/js/stacktrace.js:31\n' +
+    'printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18\n' +
+    'bar(1)@http://127.0.0.1:8000/js/file.js:13\n' +
+    'bar(2)@http://127.0.0.1:8000/js/file.js:16\n' +
+    'foo()@http://127.0.0.1:8000/js/file.js:20\n' +
+    '@http://127.0.0.1:8000/js/file.js:24\n' +
+    ''
+};
+
+CapturedExceptions.FIREFOX_7 = {
+    fileName: 'file:///G:/js/stacktrace.js',
+    lineNumber: 44,
+    stack: '()@file:///G:/js/stacktrace.js:44\n' +
+    '(null)@file:///G:/js/stacktrace.js:31\n' +
+    'printStackTrace()@file:///G:/js/stacktrace.js:18\n' +
+    'bar(1)@file:///G:/js/file.js:13\n' +
+    'bar(2)@file:///G:/js/file.js:16\n' +
+    'foo()@file:///G:/js/file.js:20\n' +
+    '@file:///G:/js/file.js:24\n' +
+    ''
+};
+
+CapturedExceptions.FIREFOX_14 = {
+    message: 'x is null',
+    stack: '@http://path/to/file.js:48\n' +
+    'dumpException3@http://path/to/file.js:52\n' +
+    'onclick@http://path/to/file.js:1\n' +
+    '',
+    fileName: 'http://path/to/file.js',
+    lineNumber: 48
+};
+
+CapturedExceptions.FIREFOX_31 = {
+    message: 'Default error',
+    name: 'Error',
+    stack: 'foo@http://path/to/file.js:41:13\n' +
+    'bar@http://path/to/file.js:1:1\n' +
+    '',
+    fileName: 'http://path/to/file.js',
+    lineNumber: 41,
+    columnNumber: 12
+};
+
+CapturedExceptions.SAFARI_6 = {
+    message: '\'null\' is not an object (evaluating \'x.undef\')',
+    stack: '@http://path/to/file.js:48\n' +
+    'dumpException3@http://path/to/file.js:52\n' +
+    'onclick@http://path/to/file.js:82\n' +
+    '[native code]',
+    line: 48,
+    sourceURL: 'http://path/to/file.js'
+};
+
+CapturedExceptions.SAFARI_7 = {
+    message: '\'null\' is not an object (evaluating \'x.undef\')',
+    name: 'TypeError',
+    stack: 'http://path/to/file.js:48:22\n' +
+    'foo@http://path/to/file.js:52:15\n' +
+    'bar@http://path/to/file.js:108:107',
+    line: 47,
+    sourceURL: 'http://path/to/file.js'
+};
+
+CapturedExceptions.SAFARI_8 = {
+    message: 'null is not an object (evaluating \'x.undef\')',
+    name: 'TypeError',
+    stack: 'http://path/to/file.js:47:22\n' +
+    'foo@http://path/to/file.js:52:15\n' +
+    'bar@http://path/to/file.js:108:23',
+    line: 47,
+    column: 22,
+    sourceURL: 'http://path/to/file.js'
+};
+
+CapturedExceptions.SAFARI_8_EVAL = {
+    message: 'Can\'t find variable: getExceptionProps',
+    name: 'ReferenceError',
+    stack: 'eval code\n' +
+    'eval@[native code]\n' +
+    'foo@http://path/to/file.js:58:21\n' +
+    'bar@http://path/to/file.js:109:91',
+    line: 1,
+    column: 18
+};
+
+CapturedExceptions.IE_9 = {
+    message: 'Unable to get property \'undef\' of undefined or null reference',
+    description: 'Unable to get property \'undef\' of undefined or null reference'
+};
+
+CapturedExceptions.IE_10 = {
+    message: 'Unable to get property \'undef\' of undefined or null reference',
+    stack: 'TypeError: Unable to get property \'undef\' of undefined or null reference\n' +
+    '   at Anonymous function (http://path/to/file.js:48:13)\n' +
+    '   at foo (http://path/to/file.js:46:9)\n' +
+    '   at bar (http://path/to/file.js:82:1)',
+    description: 'Unable to get property \'undef\' of undefined or null reference',
+    number: -2146823281
+};
+
+CapturedExceptions.IE_11 = {
+    message: 'Unable to get property \'undef\' of undefined or null reference',
+    name: 'TypeError',
+    stack: 'TypeError: Unable to get property \'undef\' of undefined or null reference\n' +
+    '   at Anonymous function (http://path/to/file.js:47:21)\n' +
+    '   at foo (http://path/to/file.js:45:13)\n' +
+    '   at bar (http://path/to/file.js:108:1)',
+    description: 'Unable to get property \'undef\' of undefined or null reference',
+    number: -2146823281
+};
+
+CapturedExceptions.IE_11_EVAL = {
+    message: '\'getExceptionProps\' is undefined',
+    name: 'ReferenceError',
+    stack: 'ReferenceError: \'getExceptionProps\' is undefined\n' +
+    '   at eval code (eval code:1:1)\n' +
+    '   at foo (http://path/to/file.js:58:17)\n' +
+    '   at bar (http://path/to/file.js:109:1)',
+    description: '\'getExceptionProps\' is undefined',
+    number: -2146823279
+};
diff --git a/src/plugins/default/ErrorPlugin-spec.ts b/src/plugins/default/ErrorPlugin-spec.ts
new file mode 100644
index 00000000..2cb09e61
--- /dev/null
+++ b/src/plugins/default/ErrorPlugin-spec.ts
@@ -0,0 +1,76 @@
+import { ContextData } from '../ContextData';
+import { EventPluginContext } from '../EventPluginContext';
+import { IEvent } from '../../models/IEvent';
+import { IErrorParser } from '../../services/IErrorParser';
+
+import { ErrorPlugin } from './ErrorPlugin';
+import { CapturedExceptions } from './ErrorPlugin-spec-exceptions';
+
+describe('ErrorPlugin', () => {
+
+  let target = new ErrorPlugin();
+  let contextData: ContextData;
+  let context: EventPluginContext;
+  let errorParser: IErrorParser;
+  let client: any;
+  let event: IEvent;
+
+  beforeEach(() => {
+    errorParser = {
+      parse: (c: EventPluginContext, exception: Error) => ({
+        type: exception.name,
+        message: exception.message
+      })
+    };
+    client = {
+      config: {
+        errorParser
+      }
+    };
+    event = {
+      data: {}
+    };
+    contextData = new ContextData();
+    context = new EventPluginContext(client, event, contextData);
+  });
+
+  describe('additional properties', () => {
+
+    describeForCapturedExceptions((exception) => {
+      beforeEach(() => {
+        contextData.setException(exception);
+      });
+
+      it('should ignore default error properties', () => {
+        target.run(context);
+        let error = event.data['@error'];
+        expect(error).toBeDefined();
+        expect(error.data && error.data['@ext']).toBeFalsy();
+      });
+    });
+
+    it('should support custom exception types', () => {
+      function NotImplementedError() {
+        this.name = 'NotImplementedError';
+        this.someProperty = 'Test';
+      }
+
+      NotImplementedError.prototype = Error.prototype;
+      contextData.setException(new NotImplementedError());
+
+      target.run(context);
+
+      let error = event.data['@error'];
+      expect(error.data['@ext'].someProperty).toBe('Test');
+    });
+  });
+});
+
+
+function describeForCapturedExceptions(specDefinitions: (exception: any) => void) {
+  let keys = Object.getOwnPropertyNames(CapturedExceptions);
+  keys.forEach(key => {
+    let exception = CapturedExceptions[key];
+    describe(key, () => { specDefinitions(exception); });
+  });
+}
diff --git a/src/plugins/default/ErrorPlugin.ts b/src/plugins/default/ErrorPlugin.ts
index 87534a42..edb2724d 100644
--- a/src/plugins/default/ErrorPlugin.ts
+++ b/src/plugins/default/ErrorPlugin.ts
@@ -2,11 +2,28 @@ import { IEventPlugin } from '../IEventPlugin';
 import { EventPluginContext } from '../EventPluginContext';
 
 export class ErrorPlugin implements IEventPlugin {
-  public priority:number = 30;
-  public name:string = 'ErrorPlugin';
+  public priority: number = 30;
+  public name: string = 'ErrorPlugin';
+  public ignoredProperties: string[] = [
+    'arguments',
+    'column',
+    'columnNumber',
+    'description',
+    'fileName',
+    'message',
+    'name',
+    'number',
+    'line',
+    'lineNumber',
+    'opera#sourceloc',
+    'sourceURL',
+    'stack',
+    'stacktrace'
+  ];
 
-  public run(context:EventPluginContext, next?:() => void): void {
-    const ERROR_KEY:string = '@error'; // optimization for minifier.
+  public run(context: EventPluginContext, next?: () => void): void {
+    const ERROR_KEY: string = '@error'; // optimization for minifier.
+    const EXTRA_PROPERTIES_KEY: string = '@ext';
 
     let exception = context.contextData.getException();
     if (!!exception) {
@@ -20,6 +37,14 @@ export class ErrorPlugin implements IEventPlugin {
 
         let result = parser.parse(context, exception);
         if (!!result) {
+          let additionalData = this.getAdditionalData(exception);
+          if (!!additionalData) {
+            if (!result.data) {
+              result.data = {};
+            }
+            result.data[EXTRA_PROPERTIES_KEY] = additionalData;
+          }
+
           context.event.data[ERROR_KEY] = result;
         }
       }
@@ -27,4 +52,24 @@ export class ErrorPlugin implements IEventPlugin {
 
     next && next();
   }
+
+  private getAdditionalData(exception: Error): { [key: string]: any } {
+    let keys = Object.keys(exception)
+      .filter(key => this.ignoredProperties.indexOf(key) < 0);
+
+    if (keys.length === 0) {
+      return null;
+    }
+
+    let additionalData = {};
+
+    keys.forEach(key => {
+      let value = exception[key];
+      if (typeof value !== 'function') {
+        additionalData[key] = value;
+      }
+    });
+
+    return additionalData;
+  }
 }
diff --git a/src/services/NodeModuleCollector.ts b/src/services/NodeModuleCollector.ts
new file mode 100644
index 00000000..3dedb324
--- /dev/null
+++ b/src/services/NodeModuleCollector.ts
@@ -0,0 +1,77 @@
+import { IModule } from '../models/IModule';
+import { IModuleCollector } from './IModuleCollector';
+import { EventPluginContext } from '../plugins/EventPluginContext';
+import { Utils } from '../Utils';
+
+import child = require('child_process');
+import path = require('path');
+
+export class NodeModuleCollector implements IModuleCollector {
+
+  private initialized:boolean = false;
+  private installedModules:{[id:string]: IModule} = {};
+
+  public getModules(context:EventPluginContext): IModule[] {
+    this.initialize();
+
+    if (!require.main) {
+      return [];
+    }
+
+    let modulePath = path.dirname(require.main.filename) + '/node_modules/';
+    let pathLength = modulePath.length;
+
+    let loadedKeys = Object.keys(require.cache);
+    let loadedModules = {};
+
+    loadedKeys.forEach(key => {
+      let id = key.substr(pathLength);
+      id = id.substr(0, id.indexOf('/'));
+      loadedModules[id] = true;
+    });
+
+    return Object.keys(loadedModules)
+      .map(key => this.installedModules[key])
+      .filter(m => m !== undefined);
+  }
+
+  private initialize() {
+    if (this.initialized) {
+      return;
+    }
+
+    this.initialized = true;
+
+    let output = child.spawnSync('npm', ['ls', '--depth=0', '--json']).stdout;
+
+    if (!output) {
+      return;
+    }
+
+    let json;
+    try {
+     json = JSON.parse(output.toString());
+    } catch (e) {
+      return;
+    }
+
+    let items = json.dependencies;
+    if (!items) {
+      return;
+    }
+
+    let id = 0;
+    this.installedModules = {};
+
+    Object.keys(items).forEach(key => {
+      let item = items[key];
+      let theModule = <IModule> {
+        module_id: id++,
+        name: key,
+        version: item.version
+      };
+
+      this.installedModules[key] = theModule;
+    });
+  }
+}
diff --git a/tslint.json b/tslint.json
index 42a949fa..cb0f720c 100644
--- a/tslint.json
+++ b/tslint.json
@@ -21,7 +21,8 @@
       "info",
       "time",
       "timeEnd",
-      "trace"
+      "trace",
+      "log"
     ],
     "no-construct": true,
     "no-debugger": true,