1/** 2 * https://github.com/csnover/TraceKit 3 * @license MIT 4 * @namespace TraceKit 5 */ 6(function(window, undefined) { 7if (!window) { 8 return; 9} 10 11var TraceKit = {}; 12var _oldTraceKit = window.TraceKit; 13 14// global reference to slice 15var _slice = [].slice; 16var UNKNOWN_FUNCTION = '?'; 17 18// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types 19var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; 20 21/** 22 * A better form of hasOwnProperty<br/> 23 * Example: `_has(MainHostObject, property) === true/false` 24 * 25 * @param {Object} object to check property 26 * @param {string} key to check 27 * @return {Boolean} true if the object has the key and it is not inherited 28 */ 29function _has(object, key) { 30 return Object.prototype.hasOwnProperty.call(object, key); 31} 32 33/** 34 * Returns true if the parameter is undefined<br/> 35 * Example: `_isUndefined(val) === true/false` 36 * 37 * @param {*} what Value to check 38 * @return {Boolean} true if undefined and false otherwise 39 */ 40function _isUndefined(what) { 41 return typeof what === 'undefined'; 42} 43 44/** 45 * Export TraceKit out to another variable<br/> 46 * Example: `var TK = TraceKit.noConflict()` 47 * @return {Object} The TraceKit object 48 * @memberof TraceKit 49 */ 50TraceKit.noConflict = function noConflict() { 51 window.TraceKit = _oldTraceKit; 52 return TraceKit; 53}; 54 55/** 56 * Wrap any function in a TraceKit reporter<br/> 57 * Example: `func = TraceKit.wrap(func);` 58 * 59 * @param {Function} func Function to be wrapped 60 * @return {Function} The wrapped func 61 * @memberof TraceKit 62 */ 63TraceKit.wrap = function traceKitWrapper(func) { 64 function wrapped() { 65 try { 66 return func.apply(this, arguments); 67 } catch (e) { 68 TraceKit.report(e); 69 throw e; 70 } 71 } 72 return wrapped; 73}; 74 75/** 76 * Cross-browser processing of unhandled exceptions 77 * 78 * Syntax: 79 * ```js 80 * TraceKit.report.subscribe(function(stackInfo) { ... }) 81 * TraceKit.report.unsubscribe(function(stackInfo) { ... }) 82 * TraceKit.report(exception) 83 * try { ...code... } catch(ex) { TraceKit.report(ex); } 84 * ``` 85 * 86 * Supports: 87 * - Firefox: full stack trace with line numbers, plus column number 88 * on top frame; column number is not guaranteed 89 * - Opera: full stack trace with line and column numbers 90 * - Chrome: full stack trace with line and column numbers 91 * - Safari: line and column number for the top frame only; some frames 92 * may be missing, and column number is not guaranteed 93 * - IE: line and column number for the top frame only; some frames 94 * may be missing, and column number is not guaranteed 95 * 96 * In theory, TraceKit should work on all of the following versions: 97 * - IE5.5+ (only 8.0 tested) 98 * - Firefox 0.9+ (only 3.5+ tested) 99 * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require 100 * Exceptions Have Stacktrace to be enabled in opera:config) 101 * - Safari 3+ (only 4+ tested) 102 * - Chrome 1+ (only 5+ tested) 103 * - Konqueror 3.5+ (untested) 104 * 105 * Requires TraceKit.computeStackTrace. 106 * 107 * Tries to catch all unhandled exceptions and report them to the 108 * subscribed handlers. Please note that TraceKit.report will rethrow the 109 * exception. This is REQUIRED in order to get a useful stack trace in IE. 110 * If the exception does not reach the top of the browser, you will only 111 * get a stack trace from the point where TraceKit.report was called. 112 * 113 * Handlers receive a TraceKit.StackTrace object as described in the 114 * TraceKit.computeStackTrace docs. 115 * 116 * @memberof TraceKit 117 * @namespace 118 */ 119TraceKit.report = (function reportModuleWrapper() { 120 var handlers = [], 121 lastException = null, 122 lastExceptionStack = null; 123 124 /** 125 * Add a crash handler. 126 * @param {Function} handler 127 * @memberof TraceKit.report 128 */ 129 function subscribe(handler) { 130 installGlobalHandler(); 131 handlers.push(handler); 132 } 133 134 /** 135 * Remove a crash handler. 136 * @param {Function} handler 137 * @memberof TraceKit.report 138 */ 139 function unsubscribe(handler) { 140 for (var i = handlers.length - 1; i >= 0; --i) { 141 if (handlers[i] === handler) { 142 handlers.splice(i, 1); 143 } 144 } 145 146 if (handlers.length === 0) { 147 window.onerror = _oldOnerrorHandler; 148 _onErrorHandlerInstalled = false;
149 } 150 } 151 152 /** 153 * Dispatch stack information to all handlers. 154 * @param {TraceKit.StackTrace} stack 155 * @param {boolean} isWindowError Is this a top-level window error? 156 * @param {Error=} error The error that's being handled (if available, null otherwise) 157 * @memberof TraceKit.report 158 * @throws An exception if an error occurs while calling an handler. 159 */ 160 function notifyHandlers(stack, isWindowError, error) { 161 var exception = null; 162 if (isWindowError && !TraceKit.collectWindowErrors) { 163 return; 164 } 165 for (var i in handlers) { 166 if (_has(handlers, i)) { 167 try { 168 handlers[i](stack, isWindowError, error); 169 } catch (inner) { 170 exception = inner; 171 } 172 } 173 } 174 175 if (exception) { 176 throw exception; 177 } 178 } 179 180 var _oldOnerrorHandler, _onErrorHandlerInstalled; 181 182 /** 183 * Ensures all global unhandled exceptions are recorded. 184 * Supported by Gecko and IE. 185 * @param {string} message Error message. 186 * @param {string} url URL of script that generated the exception. 187 * @param {(number|string)} lineNo The line number at which the error occurred. 188 * @param {(number|string)=} columnNo The column number at which the error occurred. 189 * @param {Error=} errorObj The actual Error object. 190 * @memberof TraceKit.report 191 */ 192 function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) { 193 var stack = null; 194 195 if (lastExceptionStack) { 196 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message); 197 processLastException(); 198 } else if (errorObj) { 199 stack = TraceKit.computeStackTrace(errorObj); 200 notifyHandlers(stack, true, errorObj); 201 } else { 202 var location = { 203 'url': url, 204 'line': lineNo, 205 'column': columnNo 206 }; 207 208 var name; 209 var msg = message; // must be new var or will modify original `arguments` 210 if ({}.toString.call(message) === '[object String]') { 211 var groups = message.match(ERROR_TYPES_RE); 212 if (groups) { 213 name = groups[1]; 214 msg = groups[2]; 215 } 216 } 217 218 location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line); 219 location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); 220 stack = { 221 'name': name, 222 'message': msg, 223 'mode': 'onerror', 224 'stack': [location] 225 }; 226 227 notifyHandlers(stack, true, null); 228 } 229 230 if (_oldOnerrorHandler) { 231 return _oldOnerrorHandler.apply(this, arguments); 232 } 233 234 return false; 235 } 236 237 /** 238 * Install a global onerror handler 239 * @memberof TraceKit.report 240 */ 241 function installGlobalHandler() { 242 if (_onErrorHandlerInstalled === true) { 243 return; 244 } 245 246 _oldOnerrorHandler = window.onerror; 247 window.onerror = traceKitWindowOnError; 248 _onErrorHandlerInstalled = true; 249 } 250 251 /** 252 * Process the most recent exception 253 * @memberof TraceKit.report 254 */ 255 function processLastException() { 256 var _lastExceptionStack = lastExceptionStack, 257 _lastException = lastException; 258 lastExceptionStack = null; 259 lastException = null; 260 notifyHandlers(_lastExceptionStack, false, _lastException); 261 } 262 263 /** 264 * Reports an unhandled Error to TraceKit. 265 * @param {Error} ex 266 * @memberof TraceKit.report 267 * @throws An exception if an incomplete stack trace is detected (old IE browsers). 268 */ 269 function report(ex) { 270 if (lastExceptionStack) { 271 if (lastException === ex) { 272 return; // already caught by an inner catch block, ignore 273 } else { 274 processLastException(); 275 } 276 } 277 278 var stack = TraceKit.computeStackTrace(ex); 279 lastExceptionStack = stack; 280 lastException = ex; 281 282 // If the stack trace is incomplete, wait for 2 seconds for 283 // slow slow IE to see if onerror occurs or not before reporting 284 // this exception; otherwise, we will end up with an incomplete 285 // stack trace 286 setTimeout(function () { 287 if (lastException === ex) { 288 processLastException(); 289 } 290 }, (stack.incomplete ? 2000 : 0)); 291 292 throw ex; // re-throw to propagate to the top level (and cause window.onerror) 293 } 294 295 report.subscribe = subscribe; 296 report.unsubscribe = unsubscribe; 297 return report; 298}()); 299 300/** 301 * An object representing a single stack frame. 302 * @typedef {Object} StackFrame 303 * @property {string} url The JavaScript or HTML file URL. 304 * @property {string} func The function name, or empty for anonymous functions (if guessing did n
304ot work). 305 * @property {string[]?} args The arguments passed to the function, if known. 306 * @property {number=} line The line number, if known. 307 * @property {number=} column The column number, if known. 308 * @property {string[]} context An array of source code lines; the middle element corresponds to the correct line#. 309 * @memberof TraceKit 310 */ 311 312/** 313 * An object representing a JavaScript stack trace. 314 * @typedef {Object} StackTrace 315 * @property {string} name The name of the thrown exception. 316 * @property {string} message The exception error message. 317 * @property {TraceKit.StackFrame[]} stack An array of stack frames. 318 * @property {string} mode 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace. 319 * @memberof TraceKit 320 */ 321 322/** 323 * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript 324 * 325 * Syntax: 326 * ```js 327 * s = TraceKit.computeStackTrace.ofCaller([depth]) 328 * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below) 329 * ``` 330 * 331 * Supports: 332 * - Firefox: full stack trace with line numbers and unreliable column 333 * number on top frame 334 * - Opera 10: full stack trace with line and column numbers 335 * - Opera 9-: full stack trace with line numbers 336 * - Chrome: full stack trace with line and column numbers 337 * - Safari: line and column number for the topmost stacktrace element 338 * only 339 * - IE: no line numbers whatsoever 340 * 341 * Tries to guess names of anonymous functions by looking for assignments 342 * in the source code. In IE and Safari, we have to guess source file names 343 * by searching for function bodies inside all page scripts. This will not 344 * work for scripts that are loaded cross-domain. 345 * Here be dragons: some function names may be guessed incorrectly, and 346 * duplicate functions may be mismatched. 347 * 348 * TraceKit.computeStackTrace should only be used for tracing purposes. 349 * Logging of unhandled exceptions should be done with TraceKit.report, 350 * which builds on top of TraceKit.computeStackTrace and provides better 351 * IE support by utilizing the window.onerror event to retrieve information 352 * about the top of the stack. 353 * 354 * Note: In IE and Safari, no stack trace is recorded on the Error object, 355 * so computeStackTrace instead walks its *own* chain of callers. 356 * This means that: 357 * * in Safari, some methods may be missing from the stack trace; 358 * * in IE, the topmost function in the stack trace will always be the 359 * caller of computeStackTrace. 360 * 361 * This is okay for tracing (because you are likely to be calling 362 * computeStackTrace from the function you want to be the topmost element 363 * of the stack trace anyway), but not okay for logging unhandled 364 * exceptions (because your catch block will likely be far away from the 365 * inner function that actually caused the exception). 366 * 367 * Tracing example: 368 * ```js 369 * function trace(message) { 370 * var stackInfo = TraceKit.computeStackTrace.ofCaller(); 371 * var data = message + "\n"; 372 * for(var i in stackInfo.stack) { 373 * var item = stackInfo.stack[i]; 374 * data += (item.func || '[anonymous]') + "() in " + item.url + ":" + (item.line || '0') + "\n"; 375 * } 376 * if (window.console) 377 * console.info(data); 378 * else 379 * alert(data); 380 * } 381 * ``` 382 * @memberof TraceKit 383 * @namespace 384 */ 385TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 386 var debug = false, 387 sourceCache = {}; 388 389 /** 390 * Attempts to retrieve source code via XMLHttpRequest, which is used 391 * to look up anonymous function names. 392 * @param {string} url URL of source code. 393 * @return {string} Source contents. 394 * @memberof TraceKit.computeStackTrace 395 */ 396 function loadSource(url) { 397 if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on. 398 return ''; 399 } 400 try { 401 var getXHR = function() { 402 try { 403 return new window.XMLHttpRequest(); 404 } catch (e) { 405 // explicitly bubble up the exception if not found 406 return new window.ActiveXObject('Microsoft.XMLHTTP'); 407 } 408 }; 409 410 var request = getXHR(); 411 request.open('GET', url, false); 412 request.send(''); 413 return request.responseText; 414 } catch (e) { 415 return ''; 416 } 417 } 418 419 /**
420 * Retrieves source code from the source code cache. 421 * @param {string} url URL of source code. 422 * @return {Array.<string>} Source contents. 423 * @memberof TraceKit.computeStackTrace 424 */ 425 function getSource(url) { 426 if (typeof url !== 'string') { 427 return []; 428 } 429 430 if (!_has(sourceCache, url)) { 431 // URL needs to be able to fetched within the acceptable domain. Otherwise, 432 // cross-domain errors will be triggered. 433 /* 434 Regex matches: 435 0 - Full Url 436 1 - Protocol 437 2 - Domain 438 3 - Port (Useful for internal applications) 439 4 - Path 440 */ 441 var source = ''; 442 var domain = ''; 443 try { domain = window.document.domain; } catch (e) { } 444 var match = /(.*)\:\/\/([^:\/]+)([:\d]*)\/{0,1}([\s\S]*)/.exec(url); 445 if (match && match[2] === domain) { 446 source = loadSource(url); 447 } 448 sourceCache[url] = source ? source.split('\n') : []; 449 } 450 451 return sourceCache[url]; 452 } 453 454 /** 455 * Tries to use an externally loaded copy of source code to determine 456 * the name of a function by looking at the name of the variable it was 457 * assigned to, if any. 458 * @param {string} url URL of source code. 459 * @param {(string|number)} lineNo Line number in source code. 460 * @return {string} The function name, if discoverable. 461 * @memberof TraceKit.computeStackTrace 462 */ 463 function guessFunctionName(url, lineNo) { 464 var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/, 465 reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/, 466 line = '', 467 maxLines = 10, 468 source = getSource(url), 469 m; 470 471 if (!source.length) { 472 return UNKNOWN_FUNCTION; 473 } 474 475 // Walk backwards from the first line in the function until we find the line which 476 // matches the pattern above, which is the function definition 477 for (var i = 0; i < maxLines; ++i) { 478 line = source[lineNo - i] + line; 479 480 if (!_isUndefined(line)) { 481 if ((m = reGuessFunction.exec(line))) { 482 return m[1]; 483 } else if ((m = reFunctionArgNames.exec(line))) { 484 return m[1]; 485 } 486 } 487 } 488 489 return UNKNOWN_FUNCTION; 490 } 491 492 /** 493 * Retrieves the surrounding lines from where an exception occurred. 494 * @param {string} url URL of source code. 495 * @param {(string|number)} line Line number in source code to center around for context. 496 * @return {?Array.<string>} Lines of source code. 497 * @memberof TraceKit.computeStackTrace 498 */ 499 function gatherContext(url, line) { 500 var source = getSource(url); 501 502 if (!source.length) { 503 return null; 504 } 505 506 var context = [], 507 // linesBefore & linesAfter are inclusive with the offending line. 508 // if linesOfContext is even, there will be one extra line 509 // *before* the offending line. 510 linesBefore = Math.floor(TraceKit.linesOfContext / 2), 511 // Add one extra line if linesOfContext is odd 512 linesAfter = linesBefore + (TraceKit.linesOfContext % 2), 513 start = Math.max(0, line - linesBefore - 1), 514 end = Math.min(source.length, line + linesAfter - 1); 515 516 line -= 1; // convert to 0-based index 517 518 for (var i = start; i < end; ++i) { 519 if (!_isUndefined(source[i])) { 520 context.push(source[i]); 521 } 522 } 523 524 return context.length > 0 ? context : null; 525 } 526 527 /** 528 * Escapes special characters, except for whitespace, in a string to be 529 * used inside a regular expression as a string literal. 530 * @param {string} text The string. 531 * @return {string} The escaped string literal. 532 * @memberof TraceKit.computeStackTrace 533 */ 534 function escapeRegExp(text) { 535 return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&'); 536 } 537 538 /** 539 * Escapes special characters in a string to be used inside a regular
vendor: 5,723 bytes, lines 540-705
540 * expression as a string literal. Also ensures that HTML entities will 541 * be matched the same as their literal friends. 542 * @param {string} body The string. 543 * @return {string} The escaped string. 544 * @memberof TraceKit.computeStackTrace 545 */ 546 function escapeCodeAsRegExpForMatchingInsideHTML(body) { 547 return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').replace(/\s+/g, '\\s+'); 548 } 549 550 /** 551 * Determines where a code fragment occurs in the source code. 552 * @param {RegExp} re The function definition. 553 * @param {Array.<string>} urls A list of URLs to search. 554 * @return {?Object.<string, (string|number)>} An object containing 555 * the url, line, and column number of the defined function. 556 * @memberof TraceKit.computeStackTrace 557 */ 558 function findSourceInUrls(re, urls) { 559 var source, m; 560 for (var i = 0, j = urls.length; i < j; ++i) { 561 if ((source = getSource(urls[i])).length) { 562 source = source.join('\n'); 563 if ((m = re.exec(source))) { 564 565 return { 566 'url': urls[i], 567 'line': source.substring(0, m.index).split('\n').length, 568 'column': m.index - source.lastIndexOf('\n', m.index) - 1 569 }; 570 } 571 } 572 } 573 574 return null; 575 } 576 577 /** 578 * Determines at which column a code fragment occurs on a line of the 579 * source code. 580 * @param {string} fragment The code fragment. 581 * @param {string} url The URL to search. 582 * @param {(string|number)} line The line number to examine. 583 * @return {?number} The column number. 584 * @memberof TraceKit.computeStackTrace 585 */ 586 function findSourceInLine(fragment, url, line) { 587 var source = getSource(url), 588 re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'), 589 m; 590 591 line -= 1; 592 593 if (source && source.length > line && (m = re.exec(source[line]))) { 594 return m.index; 595 } 596 597 return null; 598 } 599 600 /** 601 * Determines where a function was defined within the source code. 602 * @param {(Function|string)} func A function reference or serialized 603 * function definition. 604 * @return {?Object.<string, (string|number)>} An object containing 605 * the url, line, and column number of the defined function. 606 * @memberof TraceKit.computeStackTrace 607 */ 608 function findSourceByFunctionBody(func) { 609 if (_isUndefined(window && window.document)) { 610 return; 611 } 612 613 var urls = [window.location.href], 614 scripts = window.document.getElementsByTagName('script'), 615 body, 616 code = '' + func, 617 codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, 618 eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, 619 re, 620 parts, 621 result; 622 623 for (var i = 0; i < scripts.length; ++i) { 624 var script = scripts[i]; 625 if (script.src) { 626 urls.push(script.src); 627 } 628 } 629 630 if (!(parts = codeRE.exec(code))) { 631 re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+')); 632 } 633 634 // not sure if this is really necessary, but I don’t have a test 635 // corpus large enough to confirm that and it was in the original. 636 else { 637 var name = parts[1] ? '\\s+' + parts[1] : '', 638 args = parts[2].split(',').join('\\s*,\\s*'); 639 640 body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+'); 641 re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}'); 642 } 643 644 // look for a normal function definition 645 if ((result = findSourceInUrls(re, urls))) { 646 return result; 647 } 648 649 // look for an old-school event handler function 650 if ((parts = eventRE.exec(code))) { 651 var event = parts[1]; 652 body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]); 653 654 // look for a function defined in HTML as an onXXX handler 655 re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i'); 656 657 if ((result = findSourceInUrls(re, urls[0]))) { 658 return result; 659 } 660 661 // look for ??? 662 re = new RegExp(body); 663 664 if ((result = findSourceInUrls(re, urls))) { 665 return result; 666 } 667 } 668 669 return null; 670 } 671 672 // Contents of Exception in various browsers. 673 // 674 // SAFARI: 675 // ex.message = Can't find variable: qq 676 // ex.line = 59 677 // ex.sourceId = 580238192 678 // ex.sourceURL = http://... 679 // ex.expressionBeginOffset = 96 680 // ex.expressionCaretOffset = 98 681 // ex.expressionEndOffset = 98 682 // ex.name = ReferenceError 683 // 684 // FIREFOX: 685 // ex.message = qq is not defined 686 // ex.fileName = http://... 687 // ex.lineNumber = 59 688 // ex.columnNumber = 69 689 // ex.stack = ...stack trace... (see the example below) 690 // ex.name = ReferenceError 691 // 692 // CHROME: 693 // ex.message = qq is not defined 694 // ex.name = ReferenceError 695 // ex.type = not_defined 696 // ex.arguments = ['aa'] 697 // ex.stack = ...stack trace... 698 // 699 // INTERNET EXPLORER: 700 // ex.message = ... 701 // ex.name = ReferenceError 702 // 703 // OPERA: 704 // ex.message = ...message... (see the example below) 705 // ex.name = ReferenceError
706 // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message) 707 // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace' 708 709 /** 710 * Computes stack trace information from the stack property. 711 * Chrome and Gecko use this property. 712 * @param {Error} ex 713 * @return {?TraceKit.StackTrace} Stack trace information. 714 * @memberof TraceKit.computeStackTrace 715 */ 716 function computeStackTraceFromStackProp(ex) { 717 if (!ex.stack) { 718 return null; 719 } 720 721 var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, 722 gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, 723 winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, 724 725 // Used to additionally parse URL/line/column from eval frames 726 isEval, 727 geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, 728 chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/, 729 730 lines = ex.stack.split('\n'), 731 stack = [], 732 submatch, 733 parts, 734 element, 735 reference = /^(.*) is undefined$/.exec(ex.message); 736 737 for (var i = 0, j = lines.length; i < j; ++i) { 738 if ((parts = chrome.exec(lines[i]))) { 739 var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line 740 isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line 741 if (isEval && (submatch = chromeEval.exec(parts[2]))) { 742 // throw out eval line/column and use top-most line/column number 743 parts[2] = submatch[1]; // url 744 parts[3] = submatch[2]; // line 745 parts[4] = submatch[3]; // column 746 } 747 element = { 748 'url': !isNative ? parts[2] : null, 749 'func': parts[1] || UNKNOWN_FUNCTION, 750 'args': isNative ? [parts[2]] : [], 751 'line': parts[3] ? +parts[3] : null, 752 'column': parts[4] ? +parts[4] : null 753 }; 754 } else if ( parts = winjs.exec(lines[i]) ) { 755 element = { 756 'url': parts[2], 757 'func': parts[1] || UNKNOWN_FUNCTION, 758 'args': [], 759 'line': +parts[3], 760 'column': parts[4] ? +parts[4] : null 761 }; 762 } else if ((parts = gecko.exec(lines[i]))) { 763 isEval = parts[3] && parts[3].indexOf(' > eval') > -1; 764 if (isEval && (submatch = geckoEval.exec(parts[3]))) { 765 // throw out eval line/column and use top-most line number 766 parts[3] = submatch[1]; 767 parts[4] = submatch[2]; 768 parts[5] = null; // no column when eval 769 } else if (i === 0 && !parts[5] && !_isUndefined(ex.columnNumber)) { 770 // FireFox uses this awesome columnNumber property for its top frame 771 // Also note, Firefox's column number is 0-based and everything else expects 1-based, 772 // so adding 1 773 // NOTE: this hack doesn't work if top-most frame is eval 774 stack[0].column = ex.columnNumber + 1; 775 } 776 element = { 777 'url': parts[3], 778 'func': parts[1] || UNKNOWN_FUNCTION, 779 'args': parts[2] ? parts[2].split(',') : [], 780 'line': parts[4] ? +parts[4] : null, 781 'column': parts[5] ? +parts[5] : null 782 }; 783 } else { 784 continue; 785 } 786 787 if (!element.func && element.line) { 788 element.func = guessFunctionName(element.url, element.line); 789 } 790 791 element.context = element.line ? gatherContext(element.url, element.line) : null; 792 stack.push(element); 793 } 794 795 if (!stack.length) { 796 return null; 797 } 798 799 if (stack[0] && stack[0].line && !stack[0].column && reference) { 800 stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line); 801 } 802 803 return { 804 'mode': 'stack', 805 'name': ex.name, 806 'message': ex.message, 807 'stack': stack 808 }; 809 } 810 811 /** 812 * Computes stack trace information from the stacktrace property. 813 * Opera 10+ uses this property. 814 * @param {Error} ex 815 * @return {?TraceKit.StackTrace} Stack trace information. 816 * @memberof TraceKit.computeStackTrace 817 */ 818 function computeStackTraceFromStacktraceProp(ex) { 819 // Access and store the stacktrace property before doing ANYTHING 820 // else to it because Opera is not very good at providing it 821 // reliably in other circumstances. 822 var stacktrace = ex.stacktrace; 823 if (!stacktrace) { 824 return; 825 } 826 827 var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i, 828 opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i, 829 lines = stacktrace.split('\n'), 830 stack = [], 831 parts; 832 833 for (var line = 0; line < lines.length; line += 2) { 834 var element = null; 835 if ((parts = opera10Regex.exec(lines[line]))) { 836 element = { 837 'url': parts[2], 838 'line': +parts[1], 839 'column': null, 840 'func': parts[3], 841 'args':[] 842 }; 843 } else if ((parts = opera11Regex.exec(lines[line]))) { 844 element = { 845 'url': parts[6], 846 'line': +parts[1], 847 'column': +parts[2], 848 'func': parts[3] || parts[4], 849 'args': parts[5] ? parts[5].split(',') : [] 850 }; 851 } 852 853 if (element) { 854 if (!element.func && element.line) { 855 element.func = guessFunctionName(element.url, element.line); 856 } 857 if (element.line) { 858 try { 859 element.context = gatherContext(element.url, element.line); 860 } catch (exc) {} 861 } 862 863 if (!element.context) { 864 element.context = [lines[line + 1]]; 865 } 866 867 stack.push(element); 868 } 869 } 870 871 if (!stack.length) { 872 return null; 873 } 874 875 return { 876 'mode': 'stacktrace', 877 'name': ex.name, 878 'message': ex.message, 879 'stack': stack 880 }; 881 } 882 883 /** 884 * NOT TESTED. 885 * Computes stack trace information from an error message that includes 886 * the stack trace. 887 * Opera 9 and earlier use this method if the option to show stack 888 * traces is turned on in opera:config. 889 * @param {Error} ex 890 * @return {?TraceKit.StackTrace} Stack information. 891 * @memberof TraceKit.computeStackTrace 892 */ 893 function computeStackTraceFromOperaMultiLineMessage(ex) { 894 // TODO: Clean this function up 895 // Opera includes a stack trace into the exception message. An example is: 896 // 897 // Statement on line 3: Undefined variable: undefinedFunc 898 // Backtrace: 899 // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz 900 // undefinedFunc(a); 901 // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy 902 // zzz(x, y, z); 903 // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx 904 // yyy(a, a, a); 905 // Line 1 of function script 906 // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); } 907 // ... 908 909 var lines = ex.message.split('\n'); 910 if (lines.length < 4) { 911 return null; 912 } 913 914 var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, 915 lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, 916 lineRE3 = /^\s*Line (\d+) of function script\s*$/i, 917 stack = [], 918 scripts = (window && window.document && window.document.getElementsByTagName('script')), 919 inlineScriptBlocks = [], 920 parts; 921 922 for (var s in scripts) { 923 if (_has(scripts, s) && !scripts[s].src) { 924 inlineScriptBlocks.push(scripts[s]); 925 } 926 } 927 928 for (var line = 2; line < lines.length; line += 2) { 929 var item = null; 930 if ((parts = lineRE1.exec(lines[line]))) { 931 item = { 932 'url': parts[2], 933 'func': parts[3], 934 'args': [], 935 'line': +parts[1], 936 'column': null 937 }; 938 } else if ((parts = lineRE2.exec(lines[line]))) { 939 item = { 940 'url': parts[3], 941 'func': parts[4], 942 'args': [], 943 'line': +parts[1], 944 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number. 945 }; 946 var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block 947 var script = inlineScriptBlocks[parts[2] - 1]; 948 if (script) { 949 var source = getSource(item.url); 950 if (source) { 951 source = source.join('\n'); 952 var pos = source.indexOf(script.innerText); 953 if (pos >= 0) { 954 item.line = relativeLine + source.substring(0, pos).split('\n').length; 955 } 956 } 957 } 958 } else if ((parts = lineRE3.exec(lines[line]))) { 959 var url = window.location.href.replace(/#.*$/, ''); 960 var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1])); 961 var src = findSourceInUrls(re, [url]); 962 item = { 963 'url': url, 964 'func': '', 965 'args': [], 966 'line': src ? src.line : parts[1], 967 'column': null 968 }; 969 } 970 971 if (item) { 972 if (!item.func) { 973 item.func = guessFunctionName(item.url, item.line); 974 } 975 var context = gatherContext(item.url, item.line); 976 var midline = (context ? context[Math.floor(context.length / 2)] : null); 977 if (context && midline.replace(/^\s*/, '') === lines[line + 1].replace(/^\s*/, '')) { 978 item.context = context; 979 } else { 980 // 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); 981 item.context = [lines[line + 1]]; 982 } 983 stack.push(item); 984 } 985 } 986 if (!stack.length) { 987 return null; // could not parse multiline exception message as Opera stack trace 988 } 989 990 return { 991 'mode': 'multiline', 992 'name': ex.name, 993 'message': lines[0], 994 'stack': stack 995 }; 996 } 997 998 /** 999 * Adds information about the first frame to incomplete stack traces. 1000 * Safari and IE require this to get complete data on the first frame. 1001 * @param {TraceKit.StackTrace} stackInfo Stack trace information from 1002 * one of the compute* methods. 1003 * @param {string} url The URL of the script that caused an error. 1004 * @param {(number|string)} lineNo The line number of the script that 1005 * caused an error. 1006 * @param {string=} message The error generated by the browser, which 1007 * hopefully contains the name of the object that caused the error. 1008 * @return {boolean} Whether or not the stack information was 1009 * augmented. 1010 * @memberof TraceKit.computeStackTrace 1011 */ 1012 function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) { 1013 var initial = { 1014 'url': url, 1015 'line': lineNo 1016 }; 1017 1018 if (initial.url && initial.line) { 1019 stackInfo.incomplete = false;
vendor: 8,229 bytes, lines 1020-1277
1020 1021 if (!initial.func) { 1022 initial.func = guessFunctionName(initial.url, initial.line); 1023 } 1024 1025 if (!initial.context) { 1026 initial.context = gatherContext(initial.url, initial.line); 1027 } 1028 1029 var reference = / '([^']+)' /.exec(message); 1030 if (reference) { 1031 initial.column = findSourceInLine(reference[1], initial.url, initial.line); 1032 } 1033 1034 if (stackInfo.stack.length > 0) { 1035 if (stackInfo.stack[0].url === initial.url) { 1036 if (stackInfo.stack[0].line === initial.line) { 1037 return false; // already in stack trace 1038 } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) { 1039 stackInfo.stack[0].line = initial.line; 1040 stackInfo.stack[0].context = initial.context; 1041 return false; 1042 } 1043 } 1044 } 1045 1046 stackInfo.stack.unshift(initial); 1047 stackInfo.partial = true; 1048 return true; 1049 } else { 1050 stackInfo.incomplete = true; 1051 } 1052 1053 return false; 1054 } 1055 1056 /** 1057 * Computes stack trace information by walking the arguments.caller 1058 * chain at the time the exception occurred. This will cause earlier 1059 * frames to be missed but is the only way to get any stack trace in 1060 * Safari and IE. The top frame is restored by 1061 * {@link augmentStackTraceWithInitialElement}. 1062 * @param {Error} ex 1063 * @return {TraceKit.StackTrace=} Stack trace information. 1064 * @memberof TraceKit.computeStackTrace 1065 */ 1066 function computeStackTraceByWalkingCallerChain(ex, depth) { 1067 var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i, 1068 stack = [], 1069 funcs = {}, 1070 recursion = false, 1071 parts, 1072 item, 1073 source; 1074 1075 for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) { 1076 if (curr === computeStackTrace || curr === TraceKit.report) { 1077 continue; 1078 } 1079 1080 item = { 1081 'url': null, 1082 'func': UNKNOWN_FUNCTION, 1083 'args': [], 1084 'line': null, 1085 'column': null 1086 }; 1087 1088 if (curr.name) { 1089 item.func = curr.name; 1090 } else if ((parts = functionName.exec(curr.toString()))) { 1091 item.func = parts[1]; 1092 } 1093 1094 if (typeof item.func === 'undefined') { 1095 try { 1096 item.func = parts.input.substring(0, parts.input.indexOf('{')); 1097 } catch (e) { } 1098 } 1099 1100 if ((source = findSourceByFunctionBody(curr))) { 1101 item.url = source.url; 1102 item.line = source.line; 1103 1104 if (item.func === UNKNOWN_FUNCTION) { 1105 item.func = guessFunctionName(item.url, item.line); 1106 } 1107 1108 var reference = / '([^']+)' /.exec(ex.message || ex.description); 1109 if (reference) { 1110 item.column = findSourceInLine(reference[1], source.url, source.line); 1111 } 1112 } 1113 1114 if (funcs['' + curr]) { 1115 recursion = true; 1116 }else{ 1117 funcs['' + curr] = true; 1118 } 1119 1120 stack.push(item); 1121 } 1122 1123 if (depth) { 1124 stack.splice(0, depth); 1125 } 1126 1127 var result = { 1128 'mode': 'callers', 1129 'name': ex.name, 1130 'message': ex.message, 1131 'stack': stack 1132 }; 1133 augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description); 1134 return result; 1135 } 1136 1137 /** 1138 * Computes a stack trace for an exception. 1139 * @param {Error} ex 1140 * @param {(string|number)=} depth 1141 * @memberof TraceKit.computeStackTrace 1142 */ 1143 function computeStackTrace(ex, depth) { 1144 var stack = null; 1145 depth = (depth == null ? 0 : +depth); 1146 1147 try { 1148 // This must be tried first because Opera 10 *destroys* 1149 // its stacktrace property if you try to access the stack 1150 // property first!! 1151 stack = computeStackTraceFromStacktraceProp(ex); 1152 if (stack) { 1153 return stack; 1154 } 1155 } catch (e) { 1156 if (debug) { 1157 throw e; 1158 } 1159 } 1160 1161 try { 1162 stack = computeStackTraceFromStackProp(ex); 1163 if (stack) { 1164 return stack; 1165 } 1166 } catch (e) { 1167 if (debug) { 1168 throw e; 1169 } 1170 } 1171 1172 try { 1173 stack = computeStackTraceFromOperaMultiLineMessage(ex); 1174 if (stack) { 1175 return stack; 1176 } 1177 } catch (e) { 1178 if (debug) { 1179 throw e; 1180 } 1181 } 1182 1183 try { 1184 stack = computeStackTraceByWalkingCallerChain(ex, depth + 1); 1185 if (stack) { 1186 return stack; 1187 } 1188 } catch (e) { 1189 if (debug) { 1190 throw e; 1191 } 1192 } 1193 1194 return { 1195 'name': ex.name, 1196 'message': ex.message, 1197 'mode': 'failed' 1198 }; 1199 } 1200 1201 /** 1202 * Logs a stacktrace starting from the previous call and working down. 1203 * @param {(number|string)=} depth How many frames deep to trace. 1204 * @return {TraceKit.StackTrace} Stack trace information. 1205 * @memberof TraceKit.computeStackTrace 1206 */ 1207 function computeStackTraceOfCaller(depth) { 1208 depth = (depth == null ? 0 : +depth) + 1; // "+ 1" because "ofCaller" should drop one frame 1209 try { 1210 throw new Error(); 1211 } catch (ex) { 1212 return computeStackTrace(ex, depth + 1); 1213 } 1214 } 1215 1216 computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; 1217 computeStackTrace.computeStackTraceFromStackProp = computeStackTraceFromStackProp; 1218 computeStackTrace.guessFunctionName = guessFunctionName; 1219 computeStackTrace.gatherContext = gatherContext; 1220 computeStackTrace.ofCaller = computeStackTraceOfCaller; 1221 computeStackTrace.getSource = getSource; 1222 1223 return computeStackTrace; 1224}()); 1225 1226/** 1227 * Extends support for global error handling for asynchronous browser 1228 * functions. Adopted from Closure Library's errorhandler.js 1229 * @memberof TraceKit 1230 */ 1231TraceKit.extendToAsynchronousCallbacks = function () { 1232 var _helper = function _helper(fnName) { 1233 var originalFn = window[fnName]; 1234 window[fnName] = function traceKitAsyncExtension() { 1235 // Make a copy of the arguments 1236 var args = _slice.call(arguments); 1237 var originalCallback = args[0]; 1238 if (typeof (originalCallback) === 'function') { 1239 args[0] = TraceKit.wrap(originalCallback); 1240 } 1241 // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it 1242 // also only supports 2 argument and doesn't care what "this" is, so we 1243 // can just call the original function directly. 1244 if (originalFn.apply) { 1245 return originalFn.apply(this, args); 1246 } else { 1247 return originalFn(args[0], args[1]); 1248 } 1249 }; 1250 }; 1251 1252 _helper('setTimeout'); 1253 _helper('setInterval'); 1254}; 1255 1256//Default options: 1257if (!TraceKit.remoteFetching) { 1258 TraceKit.remoteFetching = true; 1259} 1260if (!TraceKit.collectWindowErrors) { 1261 TraceKit.collectWindowErrors = true; 1262} 1263if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) { 1264 // 5 lines before, the offending line, 5 lines after 1265 TraceKit.linesOfContext = 11; 1266} 1267 1268// UMD export 1269if (typeof define === 'function' && define.amd) { 1270 define('TraceKit', [], TraceKit); 1271} else if (typeof module !== 'undefined' && module.exports && window.module !== module) { 1272 module.exports = TraceKit; 1273} else { 1274 window.TraceKit = TraceKit; 1275} 1276 1277}(typeof window !== 'undefined' ? window : global));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.