function ErrorStackParser() {
            'use strict';

            var FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
            var CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
            var SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;

            return {
              /**
               * Given an Error object, extract the most information from it.
               * @private
               * @param {Error} error object
               * @return {Array} of stack frames
               */
              parse: function ErrorStackParser$$parse(error) {
                if (
                  typeof error.stacktrace !== 'undefined' ||
                  typeof error['opera#sourceloc'] !== 'undefined'
                ) {
                  return this.parseOpera(error);
                } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
                  return this.parseV8OrIE(error);
                } else if (error.stack) {
                  return this.parseFFOrSafari(error);
                } else {
                  // throw new Error('Cannot parse given Error object');
                }
              },

              // Separate line and column numbers from a string of the form: (URI:Line:Column)
              extractLocation: function ErrorStackParser$$extractLocation(urlLike) {
                // Fail-fast but return locations like "(native)"
                if (urlLike.indexOf(':') === -1) {
                  return [urlLike];
                }

                var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
                var parts = regExp.exec(urlLike.replace(/[()]/g, ''));
                return [parts[1], parts[2] || undefined, parts[3] || undefined];
              },

              parseV8OrIE: function ErrorStackParser$$parseV8OrIE(error) {
                var filtered = error.stack.split('\n').filter(function(line) {
                  return !!line.match(CHROME_IE_STACK_REGEXP);
                }, this);

                return filtered.map(function(line) {
                  if (line.indexOf('(eval ') > -1) {
                    // Throw away eval information until we implement stacktrace.js/stackframe#8
                    line = line
                      .replace(/eval code/g, 'eval')
                      .replace(/(\(eval at [^()]*)|(\),.*$)/g, '');
                  }
                  var sanitizedLine = line.replace(/^\s+/, '').replace(/\(eval code/g, '(');

                  // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
                  // case it has spaces in it, as the string is split on \s+ later on
                  var location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/);

                  // remove the parenthesized location from the line, if it was matched
                  sanitizedLine = location
                    ? sanitizedLine.replace(location[0], '')
                    : sanitizedLine;

                  var tokens = sanitizedLine.split(/\s+/).slice(1);
                  // if a location was matched, pass it to extractLocation() otherwise pop the last token
                  var locationParts = this.extractLocation(
                    location ? location[1] : tokens.pop()
                  );

                  var functionName = tokens.join(' ') || undefined;
                  var fileName =
                    ['eval', '<anonymous>'].indexOf(locationParts[0]) > -1
                      ? undefined
                      : locationParts[0];

                  return {
                    functionName: functionName,
                    fileName: fileName,
                    lineNumber: locationParts[1],
                    columnNumber: locationParts[2],
                    source: line
                  };
                }, this);
              },

              parseFFOrSafari: function ErrorStackParser$$parseFFOrSafari(error) {
                var filtered = error.stack.split('\n').filter(function(line) {
                  return !line.match(SAFARI_NATIVE_CODE_REGEXP);
                }, this);

                return filtered.map(function(line) {
                  // Throw away eval information until we implement stacktrace.js/stackframe#8
                  if (line.indexOf(' > eval') > -1) {
                    line = line.replace(
                      / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
                      ':$1'
                    );
                  }

                  if (line.indexOf('@') === -1 && line.indexOf(':') === -1) {
                    // Safari eval frames only have function names and nothing else
                    return {
                      functionName: line
                    };
                  } else {
                    var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
                    var matches = line.match(functionNameRegex);
                    var functionName = matches && matches[1] ? matches[1] : undefined;
                    var locationParts = this.extractLocation(
                      line.replace(functionNameRegex, '')
                    );

                    return {
                      functionName: functionName,
                      fileName: locationParts[0],
                      lineNumber: locationParts[1],
                      columnNumber: locationParts[2],
                      source: line
                    };
                  }
                }, this);
              },

              parseOpera: function ErrorStackParser$$parseOpera(e) {
                if (
                  !e.stacktrace ||
                  (e.message.indexOf('\n') > -1 &&
                    e.message.split('\n').length > e.stacktrace.split('\n').length)
                ) {
                  return this.parseOpera9(e);
                } else if (!e.stack) {
                  return this.parseOpera10(e);
                } else {
                  return this.parseOpera11(e);
                }
              },

              parseOpera9: function ErrorStackParser$$parseOpera9(e) {
                var lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
                var lines = e.message.split('\n');
                var result = [];

                for (var i = 2, len = lines.length; i < len; i += 2) {
                  var match = lineRE.exec(lines[i]);
                  if (match) {
                    result.push({
                      fileName: match[2],
                      lineNumber: match[1],
                      source: lines[i]
                    });
                  }
                }

                return result;
              },

              parseOpera10: function ErrorStackParser$$parseOpera10(e) {
                var lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
                var lines = e.stacktrace.split('\n');
                var result = [];

                for (var i = 0, len = lines.length; i < len; i += 2) {
                  var match = lineRE.exec(lines[i]);
                  if (match) {
                    result.push({
                      functionName: match[3] || undefined,
                      fileName: match[2],
                      lineNumber: match[1],
                      source: lines[i]
                    });
                  }
                }

                return result;
              },

              // Opera 10.65+ Error.stack very similar to FF/Safari
              parseOpera11: function ErrorStackParser$$parseOpera11(error) {
                var filtered = error.stack.split('\n').filter(function(line) {
                  return (
                    !!line.match(FIREFOX_SAFARI_STACK_REGEXP) &&
                    !line.match(/^Error created at/)
                  );
                }, this);

                return filtered.map(function(line) {
                  var tokens = line.split('@');
                  var locationParts = this.extractLocation(tokens.pop());
                  var functionCall = tokens.shift() || '';
                  var functionName =
                    functionCall
                      .replace(/<anonymous function(: (\w+))?>/, '$2')
                      .replace(/\([^)]*\)/g, '') || undefined;
                  var argsRaw;
                  if (functionCall.match(/\(([^)]*)\)/)) {
                    argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1');
                  }
                  var args =
                    argsRaw === undefined || argsRaw === '[arguments not available]'
                      ? undefined
                      : argsRaw.split(',');

                  return {
                    functionName: functionName,
                    args: args,
                    fileName: locationParts[0],
                    lineNumber: locationParts[1],
                    columnNumber: locationParts[2],
                    source: line
                  };
                }, this);
              }
            };
          }
Given an Error object, extract the most information from it.
@private
@param {Error} error object
@return {Array} of stack frames