diff --git a/.gitignore b/.gitignore index e81f4c6..2dbf802 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/ node_modules/ +dist/ +closure/ TraceKit.iml diff --git a/Gruntfile.js b/Gruntfile.js index f623639..b9edabf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,25 +4,31 @@ module.exports = function (grunt) { // Project configuration. grunt.initConfig({ - 'closureCompiler': { + clean: { + dist: ['dist'] + }, + closureCompiler: { options: { compilerFile: './closure/compiler.jar', checkModified: true, compilerOpts: { - compilation_level: 'ADVANCED_OPTIMIZATIONS', + compilation_level: 'SIMPLE_OPTIMIZATIONS', warning_level: 'verbose', jscomp_off: ['checkTypes', 'fileoverviewTags'], - summary_detail_level: 3, - output_wrapper: '"(function(){%output%}).call(this);"' + summary_detail_level: 3 }, execOpts: { maxBuffer: 200 * 1024 } }, - 'compile': { + compile: { src: './tracekit.js', - dest: './tracekit.min.js' + dest: './dist/tracekit.noplugins.min.js' + }, + compileWithPlugins: { + src: ['./tracekit.js', './plugins/*.js'], + dest: './dist/tracekit.min.js' } }, jshint: { @@ -59,14 +65,28 @@ module.exports = function (grunt) { ActiveXObject: false }, lint: { - src: ['grunt.js', 'tracekit.js'] + src: ['Gruntfile.js', 'tracekit.js', './plugins/*.js'] + } + }, + connect: { + test: { + options: { + port: 9001, + open: 'http://localhost:9001/tests/testBuild.html', + keepalive: true } + } } }); + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-closure-tools'); - grunt.registerTask('default', ['jshint:lint', 'closureCompiler:compile']); - grunt.registerTask('travis', ['jshint:lint', 'closureCompiler:compile']); + grunt.registerTask('default', ['clean', 'jshint:lint', 'closureCompiler']); + grunt.registerTask('travis', ['clean', 'jshint:lint', 'closureCompiler']); + + // Launch the recursion test + grunt.registerTask('test', ['default', 'connect:test']); }; diff --git a/README.md b/README.md index 7d5aac0..70203e6 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ bower install tracekit ``` This places TraceKit at `components/tracekit/tracekit.js`. Install [bower](http://twitter.github.com/bower/): `npm install bower -g`, download npm with Node: http://nodejs.org -Then include the ` + + + +

TraceKit OnError Test

+

If your browser supports the new onError signature, you should see column numbers on traces + and headers starting with 'Error', not 'undefined'.

+
+ + \ No newline at end of file diff --git a/tests/recursion.html b/tests/recursion.html index 4a6762b..5c49e72 100644 --- a/tests/recursion.html +++ b/tests/recursion.html @@ -51,7 +51,7 @@ + + + + + + + +

TraceKit Build Test

+

You should see output below and no errors (just logging) in the console. If not, the build has broken.

+
+ + + \ No newline at end of file diff --git a/tracekit.js b/tracekit.js index cea78b3..1565763 100644 --- a/tracekit.js +++ b/tracekit.js @@ -57,6 +57,40 @@ TraceKit.wrap = function traceKitWrapper(func) { return wrapped; }; +/** + * TraceKit.supportsExtendedWindowOnError: Returns a boolean indicating + * support for the extended window.onerror handler, part of the HTML5 spec as of + * August 2013. (https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror) + * + * Call this function from a plugin to determine if you should wrap a function or not. + * If the browser supports extended window.onerror, there is no need to wrap jQuery, + * setTimeout, etc. as they will receive decent stacks from window.onerror itself. + * + * An easy way to test for support is to check for an error & colno attribute + * on a created ErrorEvent. See + * https://src.chromium.org/viewvc/blink/trunk/LayoutTests/fast/events/constructors/error-event-constructor.html?r1=155454&r2=155453&pathrev=155454 + * + * @returns {Boolean} supportedExtendedOnError Support enabled/disabled boolean. + */ +TraceKit.supportsExtendedWindowOnError = function supportsExtendedWindowOnError() { + if (!window.ErrorEvent){ + return false; + } + + var testError; + try { + testError = new window.ErrorEvent('eventType', {error: {foo: 12345}}); + } catch(e){ + // IE breaks with "Object doesn't support this action" + return false; + } + + if (testError.error && testError.error.foo === 12345 && typeof testError.colno === 'number'){ + return true; + } + return false; +}; + /** * TraceKit.report: cross-browser processing of unhandled exceptions * @@ -156,7 +190,7 @@ TraceKit.report = (function reportModuleWrapper() { * @param {(number|string)} lineNo The line number at which the error * occurred. */ - function traceKitWindowOnError(message, url, lineNo) { + function traceKitWindowOnError(message, url, lineNo, colNo, error) { var stack = null; if (lastExceptionStack) { @@ -164,6 +198,9 @@ TraceKit.report = (function reportModuleWrapper() { stack = lastExceptionStack; lastExceptionStack = null; lastException = null; + } else if (error) { + // New HTML5 spec (Aug 2013) actually passes an error to window.onerror + stack = TraceKit.computeStackTrace(error); } else { var location = { 'url': url, @@ -1063,35 +1100,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return computeStackTrace; }()); -/** - * Extends support for global error handling for asynchronous browser - * functions. Adopted from Closure Library's errorhandler.js - */ -(function extendToAsynchronousCallbacks() { - var _helper = function _helper(fnName) { - var originalFn = window[fnName]; - window[fnName] = function traceKitAsyncExtension() { - // Make a copy of the arguments - var args = _slice.call(arguments); - var originalCallback = args[0]; - if (typeof (originalCallback) === 'function') { - args[0] = TraceKit.wrap(originalCallback); - } - // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it - // also only supports 2 argument and doesn't care what "this" is, so we - // can just call the original function directly. - if (originalFn.apply) { - return originalFn.apply(this, args); - } else { - return originalFn(args[0], args[1]); - } - }; - }; - - _helper('setTimeout'); - _helper('setInterval'); -}()); - //Default options: if (!TraceKit.remoteFetching) { TraceKit.remoteFetching = true; @@ -1103,6 +1111,10 @@ if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) { // 5 lines before, the offending line, 5 lines after TraceKit.linesOfContext = 11; } +// Set to true to ignore e.g. plugin warnings +if (typeof TraceKit.suppressWarnings === 'undefined') { + TraceKit.suppressWarnings = false; +}