diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8fbe3ac --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "attach", + "name": "Attach Karma Chrome", + "address": "localhost", + "port": 9333, + "pathMapping": { + "/": "${workspaceRoot}/", + "/base/": "${workspaceRoot}/" + }, + "webRoot": "${workspaceRoot}/" + }, + { + "type": "chrome", + "request": "launch", + "name": "Test", + "sourceMaps": true, + "webRoot": "${workspaceRoot}/", + "url": "http://localhost:9333/debug.html", + "runtimeArgs": [ + "--headless" + ] + } + ] +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index b4f944b..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,66 +0,0 @@ -/*global module:false*/ -module.exports = function (grunt) { - 'use strict'; - - /** - * Bypass grunt-bump limitation - * see https://github.com/vojtajina/grunt-bump/pull/189 - */ - var gruntBumpPrereleaseName = 'rc'; - grunt.initConfig({ - bump: { - options: { - files: ['package.json', 'bower.json', 'appveyor.yml'], - prereleaseName: gruntBumpPrereleaseName, - /** - * Need to create a new RegExp for appveyor - * https://github.com/vojtajina/grunt-bump/issues/190 - */ - regExp: new RegExp( - '([\'|\"]?version[\'|\"]?[ ]*:[ ]*[\'|\"]?)(\\d+\\.\\d+\\.\\d+(-' + - gruntBumpPrereleaseName + - '\\.\\d+)?(-\\d+)?)[\\d||A-a|-]*([\'|\"]?)', 'i' - ) - } - }, - jshint: { - options: { - jshintrc: '.jshintrc' - }, - lint: { - src: [ - 'grunt.js', - 'tracekit.js' - ] - } - }, - jasmine : { - src: [ - 'tracekit.js', - 'spec/fixtures/captured-errors.js' - ], - options: { - specs: 'spec/*-spec.js' - } - }, - jsdoc : { - dist: { - src: ['tracekit.js'], - options: { - destination: 'doc', - readme: 'README.md', - configure: 'jsdoc.conf.json' - } - } - } - }); - - grunt.loadNpmTasks('grunt-contrib-jasmine'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-bump'); - grunt.loadNpmTasks('grunt-jsdoc'); - - grunt.registerTask('doc', ['jsdoc']); - grunt.registerTask('test', ['jasmine']); - grunt.registerTask('default', ['jshint:lint']); -}; diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..80c4030 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,73 @@ +/* jshint esversion: 6 */ +/* jshint node: true */ +const gulp = require('gulp'); +const Server = require('karma').Server; +const jshint = require('gulp-jshint'); +const bump = require('gulp-bump'); + +const srcCode = ['./tracekit.js']; +/** + * Run test once and exit + */ +gulp.task('test', function (done) { + new Server({ + configFile: __dirname + '/karma.conf.js', + singleRun: true + }, done).start(); +}); + +/** + * Watch for file changes and re-run tests on each change + */ +gulp.task('tdd', function (done) { + new Server({ + configFile: __dirname + '/karma.conf.js' + }, done).start(); +}); + +/** + * Watch for file changes and re-run tests on each change + */ +gulp.task('tddchrome', function (done) { + new Server({ + configFile: __dirname + '/karma.conf.chrome.js' + }, done).start(); +}); + +// We do this over using include/exclude to make everything feel gulp-like! +gulp.task('doc', function (cb) { + let jsdoc = require('gulp-jsdoc3'); + + let config = require('./jsdoc.conf.json'); + gulp.src(['README.md'].concat(srcCode), { + read: false + }) + .pipe(jsdoc(config, cb)); +}); + + +gulp.task('lint', function () { + return gulp.src(['./tracekit.js', './Gulpfile.js']) + .pipe(jshint()) + .pipe(jshint.reporter('default')); +}); + + +// Update bower, component, npm at once: +gulp.task('bump', function () { + gulp.src(['package.json', 'bower.json', 'appveyor.yml']) + .pipe(bump({ + type: 'patch' + })) + .pipe(gulp.dest('./')); +}); + + +// Update bower, component, npm at once: +gulp.task('bump-minor', function () { + gulp.src(['package.json', 'bower.json', 'appveyor.yml']) + .pipe(bump({ + type: 'minor' + })) + .pipe(gulp.dest('./')); +}); diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..259a24e --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-tactile \ No newline at end of file diff --git a/jsdoc.conf.json b/jsdoc.conf.json index 82cec7e..b79edc5 100644 --- a/jsdoc.conf.json +++ b/jsdoc.conf.json @@ -5,5 +5,9 @@ }, "plugins": [ "plugins/markdown" - ] + ], + "opts": { + "destination": "./doc/", + "recurse": true + } } diff --git a/karma.conf.chrome.js b/karma.conf.chrome.js new file mode 100644 index 0000000..24cf545 --- /dev/null +++ b/karma.conf.chrome.js @@ -0,0 +1,75 @@ +// Karma configuration + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + plugins: [ + 'karma-phantomjs-launcher', + 'karma-jasmine', + 'karma-chrome-launcher' + ], + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'tracekit.js', + 'spec/**/*.js' + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9333, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['ChromeDebugging'], + + customLaunchers: { + ChromeDebugging: { + base: 'Chrome', + flags: ['--remote-debugging-prt=9333'], + debug: true + } + }, + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }) +}; diff --git a/karma.conf.js b/karma.conf.js index f2d3835..b5fd4fc 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -37,7 +37,7 @@ module.exports = function(config) { // web server port - port: 9876, + port: 9333, // enable / disable colors in the output (reporters and logs) diff --git a/package.json b/package.json index 1f3218c..23c30fe 100644 --- a/package.json +++ b/package.json @@ -17,22 +17,25 @@ ], "license": "MIT", "devDependencies": { - "grunt": "1.0.1", - "grunt-bump": "^0.8.0", - "grunt-cli": "1.2.0", - "grunt-contrib-jasmine": "1.1.0", - "grunt-contrib-jshint": "1.1.0", - "grunt-jsdoc": "2.1.0", - "jasmine": "2.6.0", - "jasmine-core": "2.6.4", - "karma": "1.7.0", - "karma-jasmine": "1.1.0", + "gulp": "3.9.1", + "gulp-jsdoc3": "^2.0.0", + "gulp-jshint": "^2.1.0", + "jasmine": "3.1.0", + "jasmine-core": "3.1.0", + "jsdoc": "3.5.5", + "jshint": "^2.9.5", + "karma": "2.0.4", + "karma-chrome-launcher": "2.2.0", + "karma-jasmine": "1.1.2", "karma-phantomjs-launcher": "1.0.4", - "phantomjs-prebuilt": "2.1.14" + "phantomjs-prebuilt": "2.1.16" }, "scripts": { - "test": "grunt test" + "test": "gulp test" }, "typings": "tracekit.d.ts", - "author": "Blake Niemyjski " + "author": "Blake Niemyjski ", + "dependencies": { + "gulp-bump": "^3.1.1" + } } diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..11ae489 --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,10 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "*[sS]pec.js" + ], + "helpers": [ + "../spec/fixtures/captured-errors.js", + "../tracekit.js" + ] +} \ No newline at end of file diff --git a/spec/tracekit-spec.js b/spec/tracekit-spec.js index 666d816..1a6d194 100644 --- a/spec/tracekit-spec.js +++ b/spec/tracekit-spec.js @@ -129,7 +129,9 @@ if (message === testMessage || lineNo === testLineNo) { return true; } - return oldOnErrorHandler.apply(this, arguments); + if (oldOnErrorHandler) { + return oldOnErrorHandler.apply(this, arguments); + } }; }); diff --git a/tracekit.d.ts b/tracekit.d.ts index 7c03476..0a775e7 100644 --- a/tracekit.d.ts +++ b/tracekit.d.ts @@ -7,6 +7,22 @@ export interface StackFrame { context:string[]; } +declare global { + interface Window { + onunhandledrejection: PromiseRejectionEvent; + _onErrorHandlerInstalled : boolean; + } + interface Error { + columnNumber: number; + stacktrace : string; + description : string; + sourceURL : string; + fileName: string; + line : number; + lineNumber : number; + } +} + export interface StackTrace { /** * Known modes: callers, failed, multiline, onerror, stack, stacktrace @@ -17,8 +33,19 @@ export interface StackTrace { url:string; stack:StackFrame[]; useragent:string; + incomplete?: boolean; + partial?: boolean; +} + +export interface TraceKit { + /** + * stacktrace + */ + StackTrace:StackTrace; } + + interface ComputeStackTrace { /** * Computes a stack trace for an exception. diff --git a/tracekit.js b/tracekit.js index ceafd9c..728448f 100644 --- a/tracekit.js +++ b/tracekit.js @@ -1,9 +1,10 @@ +/// /** * https://github.com/csnover/TraceKit * @license MIT * @namespace TraceKit */ -(function(window, undefined) { +(function (window, undefined) { if (!window) { return; } @@ -17,7 +18,6 @@ var UNKNOWN_FUNCTION = '?'; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; - /** * A better form of hasOwnProperty
* Example: `_has(MainHostObject, property) === true/false` @@ -121,52 +121,85 @@ TraceKit.report = (function reportModuleWrapper() { lastException = null, lastExceptionStack = null; + /** + * + * @param {*} newOnErrorHandler + * @param {*} errorWindow + * @param {*} oldOnErrorHandler + * @param {*} oldOnunhandledrejection + */ + function HandlerClass(newOnErrorHandler, errorWindow, oldOnErrorHandler, oldOnunhandledrejection) { + this.handler = newOnErrorHandler; + this.window = errorWindow; + this.oldOnErrorHandler = oldOnErrorHandler; + this.oldOnunhandledrejection = oldOnunhandledrejection; + } + + /** + * Can the window be used + * @param {Window} win + * @returns {boolean} + * @memberof TraceKit.report + */ + function isWindowAccessible(win) { + try { + return (win.location.href !==''); + } catch (e) { + return false; + } + } /** * Add a crash handler. * @param {Function} handler + * @param {Window} win default is current window. Need if you want to subcribe tracekit to another window/frame * @memberof TraceKit.report */ - function subscribe(handler) { - installGlobalHandler(); - installGlobalUnhandledRejectionHandler(); - handlers.push(handler); - } + function subscribe(handler, win) { + win = (win || window); + if (isWindowAccessible(win)) { + TraceKit.windowPointer = win; + installGlobalHandler(handler, win); + } + } /** * Remove a crash handler. * @param {Function} handler + * @param {Window} win default is current window. Need if you want to unsubcribe tracekit to another window/frame * @memberof TraceKit.report */ - function unsubscribe(handler) { - for (var i = handlers.length - 1; i >= 0; --i) { - if (handlers[i] === handler) { - handlers.splice(i, 1); + function unsubscribe(handler, win) { + win = (win || window); + if (isWindowAccessible(win)) { + for (var i = handlers.length - 1; i >= 0; --i) { + if (handlers[i].handler === handler && win === handlers[i].window) { + // put back the old event handler + uninstallGlobalHandler(handlers[i]); + // remove handler from handlers + handlers.splice(i, 1); + } } } - - if (handlers.length === 0) { - uninstallGlobalHandler(); - uninstallGlobalUnhandledRejectionHandler(); - } } /** * Dispatch stack information to all handlers. - * @param {TraceKit.StackTrace} stack + * @param {StackTrace} stack * @param {boolean} isWindowError Is this a top-level window error? - * @param {Error=} error The error that's being handled (if available, null otherwise) + * @param {array} args all the arguments from onerror event. Array of [Message, url, lineNo, columnNo, errorObj] * @memberof TraceKit.report * @throws An exception if an error occurs while calling an handler. */ - function notifyHandlers(stack, isWindowError, error) { + function notifyHandlers(stack, isWindowError, args) { var exception = null; - if (isWindowError && !TraceKit.collectWindowErrors) { - return; + if (isWindowError && !TraceKit.collectWindowErrors || handlers.length===0) { + return; } for (var i in handlers) { - if (_has(handlers, i)) { + if (_has(handlers, i) && handlers[i].window === TraceKit.windowPointer) { try { - handlers[i](stack, isWindowError, error); + var errorObj = (args.length > 4) ? args[4] : args; + handlers[i].handler(stack, isWindowError, errorObj); } catch (inner) { exception = inner; } @@ -178,35 +211,31 @@ TraceKit.report = (function reportModuleWrapper() { } } - var _oldOnerrorHandler, _onErrorHandlerInstalled; - var _oldOnunhandledrejectionHandler, _onUnhandledRejectionHandlerInstalled; - /** * Ensures all global unhandled exceptions are recorded. * Supported by Gecko and IE. * @param {string} message Error message. * @param {string} url URL of script that generated the exception. - * @param {(number|string)} lineNo The line number at which the error occurred. + * @param {number} lineNo The line number at which the error occurred. * @param {(number|string)=} columnNo The column number at which the error occurred. * @param {Error=} errorObj The actual Error object. * @memberof TraceKit.report */ function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) { - var stack = null; - + var stack = {}; + TraceKit.windowPointer = this; if (lastExceptionStack) { TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message); - processLastException(); + processLastException(); } else if (errorObj) { stack = TraceKit.computeStackTrace(errorObj); - notifyHandlers(stack, true, errorObj); + notifyHandlers(stack, true, Array.prototype.slice.call(arguments)); } else { var location = { - 'url': url, - 'line': lineNo, - 'column': columnNo + 'url': url, + 'line': lineNo, + 'column': columnNo }; - var name; var msg = message; // must be new var or will modify original `arguments` if ({}.toString.call(message) === '[object String]') { @@ -216,22 +245,21 @@ TraceKit.report = (function reportModuleWrapper() { msg = groups[2]; } } - location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line); location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); stack = { 'name': name, 'message': msg, 'mode': 'onerror', - 'stack': [location] + 'stack': [location], + incomplete: false, + partial: false }; - notifyHandlers(stack, true, null); + notifyHandlers(stack, true, Array.prototype.slice.call(arguments)); } - if (_oldOnerrorHandler) { - return _oldOnerrorHandler.apply(this, arguments); - } + return false; } @@ -250,51 +278,33 @@ TraceKit.report = (function reportModuleWrapper() { /** * Install a global onerror handler + * @param {Function} handler + * @param {Window} win tracekit will be attached to this window * @memberof TraceKit.report */ - function installGlobalHandler() { - if (_onErrorHandlerInstalled === true) { + function installGlobalHandler(handler, win) { + if (win._onErrorHandlerInstalled === true) { return; } - - _oldOnerrorHandler = window.onerror; - window.onerror = traceKitWindowOnError; - _onErrorHandlerInstalled = true; + var oldOnerrorHandler = win.onerror; + var oldOnunhandledrejection = win.onunhandledrejection; + win.onerror = traceKitWindowOnError; + win.onunhandledrejection = traceKitWindowOnUnhandledRejection; + win._onErrorHandlerInstalled = true; + handlers.push(new HandlerClass(handler, win, oldOnerrorHandler, oldOnunhandledrejection)); } /** * Uninstall the global onerror handler + * @param {HandlerClass} handler * @memberof TraceKit.report */ - function uninstallGlobalHandler() { - if (_onErrorHandlerInstalled) { - window.onerror = _oldOnerrorHandler; - _onErrorHandlerInstalled = false; - } - } - - /** - * Install a global onunhandledrejection handler - * @memberof TraceKit.report - */ - function installGlobalUnhandledRejectionHandler() { - if (_onUnhandledRejectionHandlerInstalled === true) { - return; - } - - _oldOnunhandledrejectionHandler = window.onunhandledrejection; - window.onunhandledrejection = traceKitWindowOnUnhandledRejection; - _onUnhandledRejectionHandlerInstalled = true; - } - - /** - * Uninstall the global onunhandledrejection handler - * @memberof TraceKit.report - */ - function uninstallGlobalUnhandledRejectionHandler() { - if (_onUnhandledRejectionHandlerInstalled) { - window.onerror = _oldOnunhandledrejectionHandler; - _onUnhandledRejectionHandlerInstalled = false; + function uninstallGlobalHandler(handler) { + var win = handler.window; + if (win._onErrorHandlerInstalled) { + win.onerror = handler.oldOnErrorHandler; + win.onunhandledrejection = handler.oldOnunhandledrejection; + win._onErrorHandlerInstalled = false; } } @@ -321,7 +331,7 @@ TraceKit.report = (function reportModuleWrapper() { if (lastException === ex) { return; // already caught by an inner catch block, ignore } else { - processLastException(); + processLastException(); } } @@ -333,7 +343,7 @@ TraceKit.report = (function reportModuleWrapper() { // slow slow IE to see if onerror occurs or not before reporting // this exception; otherwise, we will end up with an incomplete // stack trace - setTimeout(function () { + window.setTimeout(function () { if (lastException === ex) { processLastException(); } @@ -364,8 +374,10 @@ TraceKit.report = (function reportModuleWrapper() { * @typedef {Object} StackTrace * @property {string} name The name of the thrown exception. * @property {string} message The exception error message. - * @property {TraceKit.StackFrame[]} stack An array of stack frames. + * @property {StackFrame[]} stack An array of stack frames. * @property {string} mode 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace. + * @property {boolean?} incomplete + * @property {boolean?} partial * @memberof TraceKit */ @@ -434,7 +446,8 @@ TraceKit.report = (function reportModuleWrapper() { */ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { var debug = false, - sourceCache = {}; + sourceCache = {}, + curWin = TraceKit.windowPointer; /** * Attempts to retrieve source code via XMLHttpRequest, which is used @@ -448,7 +461,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return ''; } try { - var getXHR = function() { + var getXHR = function () { try { return new window.XMLHttpRequest(); } catch (e) { @@ -490,7 +503,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { */ var source = ''; var domain = ''; - try { domain = window.document.domain; } catch (e) { } + try { + domain = curWin.document.domain; + } catch (e) {} var match = /(.*)\:\/\/([^:\/]+)([:\d]*)\/{0,1}([\s\S]*)/.exec(url); if (match && match[2] === domain) { source = loadSource(url); @@ -506,7 +521,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * the name of a function by looking at the name of the variable it was * assigned to, if any. * @param {string} url URL of source code. - * @param {(string|number)} lineNo Line number in source code. + * @param {(number)} lineNo Line number in source code. * @return {string} The function name, if discoverable. * @memberof TraceKit.computeStackTrace */ @@ -542,7 +557,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { /** * Retrieves the surrounding lines from where an exception occurred. * @param {string} url URL of source code. - * @param {(string|number)} line Line number in source code to center around for context. + * @param {number} line Line number in source code to center around for context. * @return {?Array.} Lines of source code. * @memberof TraceKit.computeStackTrace */ @@ -621,6 +636,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } } + return null; } @@ -629,7 +645,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * source code. * @param {string} fragment The code fragment. * @param {string} url The URL to search. - * @param {(string|number)} line The line number to examine. + * @param {number} line The line number to examine. * @return {?number} The column number. * @memberof TraceKit.computeStackTrace */ @@ -656,12 +672,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @memberof TraceKit.computeStackTrace */ function findSourceByFunctionBody(func) { - if (_isUndefined(window && window.document)) { + if (_isUndefined(curWin && curWin.document)) { return; } - var urls = [window.location.href], - scripts = window.document.getElementsByTagName('script'), + var urls = [curWin.location.href], + scripts = curWin.document.getElementsByTagName('script'), body, code = '' + func, codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, @@ -760,7 +776,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Computes stack trace information from the stack property. * Chrome and Gecko use this property. * @param {Error} ex - * @return {?TraceKit.StackTrace} Stack trace information. + * @return {?StackTrace} Stack trace information. * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromStackProp(ex) { @@ -771,17 +787,15 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, - // Used to additionally parse URL/line/column from eval frames isEval, geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/, - lines = ex.stack.split('\n'), stack = [], submatch, parts, - element, + element = {}, reference = /^(.*) is undefined$/.exec(ex.message); for (var i = 0, j = lines.length; i < j; ++i) { @@ -801,7 +815,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'line': parts[3] ? +parts[3] : null, 'column': parts[4] ? +parts[4] : null }; - } else if ( parts = winjs.exec(lines[i]) ) { + } else if (parts = winjs.exec(lines[i])) { element = { 'url': parts[2], 'func': parts[1] || UNKNOWN_FUNCTION, @@ -827,7 +841,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'url': parts[3], 'func': parts[1] || UNKNOWN_FUNCTION, 'args': parts[2] ? parts[2].split(',') : [], - 'line': parts[4] ? +parts[4] : null, + 'line': parts[4] ? parseInt(parts[4]) : null, 'column': parts[5] ? +parts[5] : null }; } else { @@ -839,6 +853,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } element.context = element.line ? gatherContext(element.url, element.line) : null; + stack.push(element); } @@ -851,10 +866,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } return { - 'mode': 'stack', - 'name': ex.name, - 'message': ex.message, - 'stack': stack + mode: 'stack', + name: ex.name, + message: ex.message, + stack: stack, + incomplete: false, + partial: false }; } @@ -862,7 +879,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Computes stack trace information from the stacktrace property. * Opera 10+ uses this property. * @param {Error} ex - * @return {?TraceKit.StackTrace} Stack trace information. + * @return {?StackTrace} Stack trace information. * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromStacktraceProp(ex) { @@ -888,7 +905,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'line': +parts[1], 'column': null, 'func': parts[3], - 'args':[] + 'args': [] }; } else if ((parts = opera11Regex.exec(lines[line]))) { element = { @@ -937,7 +954,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Opera 9 and earlier use this method if the option to show stack * traces is turned on in opera:config. * @param {Error} ex - * @return {?TraceKit.StackTrace} Stack information. + * @return {?StackTrace} Stack information. * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromOperaMultiLineMessage(ex) { @@ -965,7 +982,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, lineRE3 = /^\s*Line (\d+) of function script\s*$/i, stack = [], - scripts = (window && window.document && window.document.getElementsByTagName('script')), + scripts = (curWin && curWin.document && curWin.document.getElementsByTagName('script')), inlineScriptBlocks = [], parts; @@ -1006,7 +1023,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } } } else if ((parts = lineRE3.exec(lines[line]))) { - var url = window.location.href.replace(/#.*$/, ''); + var url = curWin.location.href.replace(/#.*$/, ''); var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1])); var src = findSourceInUrls(re, [url]); item = { @@ -1110,7 +1127,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Safari and IE. The top frame is restored by * {@link augmentStackTraceWithInitialElement}. * @param {Error} ex - * @return {TraceKit.StackTrace=} Stack trace information. + * @return {StackTrace=} Stack trace information. * @memberof TraceKit.computeStackTrace */ function computeStackTraceByWalkingCallerChain(ex, depth) { @@ -1142,9 +1159,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } if (typeof item.func === 'undefined') { - try { - item.func = parts.input.substring(0, parts.input.indexOf('{')); - } catch (e) { } + try { + item.func = parts.input.substring(0, parts.input.indexOf('{')); + } catch (e) {} } if ((source = findSourceByFunctionBody(curr))) { @@ -1163,7 +1180,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { if (funcs['' + curr]) { recursion = true; - }else{ + } else { funcs['' + curr] = true; } @@ -1191,6 +1208,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @memberof TraceKit.computeStackTrace */ function computeStackTrace(ex, depth) { + curWin = TraceKit.windowPointer; var stack = null; depth = (depth == null ? 0 : +depth); @@ -1280,8 +1298,8 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { */ TraceKit.extendToAsynchronousCallbacks = function () { var _helper = function _helper(fnName) { - var originalFn = window[fnName]; - window[fnName] = function traceKitAsyncExtension() { + var originalFn = TraceKit.windowPointer[fnName]; + TraceKit.windowPointer[fnName] = function traceKitAsyncExtension() { // Make a copy of the arguments var args = _slice.call(arguments); var originalCallback = args[0]; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..90ee79e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "lib": ["es5", "es6", "dom"], + "allowJs": true, + "checkJs": true + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tutorials/Iframes.html b/tutorials/Iframes.html new file mode 100644 index 0000000..63e750c --- /dev/null +++ b/tutorials/Iframes.html @@ -0,0 +1,82 @@ +

IFrame solution

+ +

This should be used for iframe pages. Only include this code and it will inject javascript into any frames.

+ +
+/**    
+* Description    
+* @param {type} aTracefunc Description    
+*/
+function insertTraceKit(aTracefunc) {
+
+ TraceKit.report.subscribe(aTracefunc);
+
+ function wrapped(aThis) {
+   var w = (aThis.target || aThis);
+
+   function docUnloadChange(e) {
+     TraceKit.report.unsubscribe(yourLogger, e.currentTarget);
+     //console.log('unload', e.currentTarget.location.href, e.currentTarget.readyState);
+     var l = e.currentTarget.location.href;
+     var checkForUnload = function (e) {
+       try {
+         if (l != e.location.href) {
+           clearInterval(unloadCheckInterval);
+           //console.log(e.location.href);
+           TraceKit.report.subscribe(aTracefunc, e);
+         }
+       } catch (exception) {
+         clearInterval(unloadCheckInterval);
+         unloadCheckInterval = setInterval(checkForUnload, 100, this);
+         //console.log('cross-origin frame detected');
+       }
+     };
+     var unloadCheckInterval = setInterval(checkForUnload, 1, this);
+
+   }
+
+   TraceKit.report.subscribe(aTracefunc, w.contentWindow);
+   try {
+     w.contentWindow.addEventListener("unload", docUnloadChange);
+   } catch (e) {
+     //console.log('cross-origin frame detected');
+   }
+ }
+
+ var frames = document.querySelectorAll('frame,iframe');
+ for (var i = 0; i < frames.length; i++) {
+   frames[i].onload = wrapped;
+   try {
+     // console.log(frames[i].contentDocument.readyState, frames[i].contentDocument.location.href);
+     frames[i].readyStateCheckInterval = setInterval(fasteractiveTrace, 1, frames[i]);
+   } catch (e) {
+     //console.log('cross-origin frame detected');
+   }
+ }
+
+ function fasteractiveTrace(e) {
+   try {
+     if (e.contentDocument) {
+       if (e.contentDocument.readyState == "complete" || e.contentDocument.readyState == "interactive") {
+         wrapped(e);
+         clearInterval(e.readyStateCheckInterval);
+       }
+       // console.log(e.contentDocument.readyState, e.contentDocument.location.href);
+     }
+   } catch (exception) {
+     //console.log('cross-origin frame detected');
+   }
+ }
+
+}
+
+var readyStateCheckInterval = setInterval(waitInit, 10, this);
+
+function waitInit() {
+ //console.log(document.readyState);
+ if (document.readyState === "complete" || document.readyState === "interactive") {
+   clearInterval(readyStateCheckInterval);
+   insertTraceKit(yourLogger);
+ }
+}
+
\ No newline at end of file