From a14ed8aa2ede3c36c10a9a0341ec5166d202cb34 Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Tue, 4 Nov 2014 19:30:15 -0500 Subject: [PATCH] add console polyfill; kill stacktrace.js --- README | 1 - bower.json | 1 - docs/legal.html | 1 - dygraph-dev.js | 2 +- dygraph-internal.externs.js | 6 - dygraph-utils.js | 2 +- generate-combined.sh | 1 + phantom-driver.js | 3 - polyfills/console.js | 15 ++ push-to-web.sh | 4 +- stacktrace.js | 411 -------------------------------------------- 11 files changed, 20 insertions(+), 427 deletions(-) create mode 100644 polyfills/console.js delete mode 100644 stacktrace.js diff --git a/README b/README index d817eb3..010526e 100644 --- a/README +++ b/README @@ -53,7 +53,6 @@ dygraphs uses: - excanvas.js (Apache License) - YUI compressor (BSD License) - JsDoc Toolkit (MIT license) - - stacktrace.js is public domain automated tests use: - auto_tests/lib/jquery-1.4.2.js (MIT & GPL2) diff --git a/bower.json b/bower.json index 4aa054d..9cb1240 100644 --- a/bower.json +++ b/bower.json @@ -36,7 +36,6 @@ "release.sh", "releases.json", "screenshot.png", - "stacktrace.js", "test.sh", "tests", "thumbnail.png", diff --git a/docs/legal.html b/docs/legal.html index 3e3d176..995076a 100644 --- a/docs/legal.html +++ b/docs/legal.html @@ -10,7 +10,6 @@ - excanvas.js (Apache License) - YUI compressor (BSD License) - JsDoc Toolkit (MIT license) - - stacktrace.js is public domain automated tests use: - auto_tests/lib/jquery-1.4.2.js (MIT & GPL2) diff --git a/dygraph-dev.js b/dygraph-dev.js index 6adfa51..26a96df 100644 --- a/dygraph-dev.js +++ b/dygraph-dev.js @@ -16,7 +16,7 @@ // This list needs to be kept in sync w/ the one in generate-combined.sh // and the one in jsTestDriver.conf. var source_files = [ - "stacktrace.js", + "polyfills/console.js", "dashed-canvas.js", "dygraph-options.js", "dygraph-layout.js", diff --git a/dygraph-internal.externs.js b/dygraph-internal.externs.js index 92e5cc3..dabfa10 100644 --- a/dygraph-internal.externs.js +++ b/dygraph-internal.externs.js @@ -2,12 +2,6 @@ // - declares symbols that are provided outisde of dygraphs (e.g. by excanvas) // - defines custom types used internally -/** - * @param {Object} dict - * @return {!Array.} - */ -function printStackTrace(dict) {} - /** * @constructor diff --git a/dygraph-utils.js b/dygraph-utils.js index 9158aa5..94673e0 100644 --- a/dygraph-utils.js +++ b/dygraph-utils.js @@ -12,7 +12,7 @@ */ /*jshint globalstrict: true */ -/*global Dygraph:false, G_vmlCanvasManager:false, Node:false, printStackTrace: false */ +/*global Dygraph:false, G_vmlCanvasManager:false, Node:false */ "use strict"; Dygraph.LOG_SCALE = 10; diff --git a/generate-combined.sh b/generate-combined.sh index 7cdc22a..30c0a7e 100755 --- a/generate-combined.sh +++ b/generate-combined.sh @@ -5,6 +5,7 @@ GetSources () { # This list needs to be kept in sync w/ the one in dygraph-dev.js # and the one in jsTestDriver.conf. Order matters, except for the plugins. for F in \ + polyfills/console.js \ dashed-canvas.js \ dygraph-options.js \ dygraph-layout.js \ diff --git a/phantom-driver.js b/phantom-driver.js index 22f4687..c534964 100644 --- a/phantom-driver.js +++ b/phantom-driver.js @@ -63,9 +63,6 @@ page.open(url, function(status) { results = page.evaluate(function() { var num_passing = 0, num_failing = 0; var failures = []; - // Phantom doesn't like stacktrace.js using the "arguments" object - // in stacktrace.js, which it interprets in strict mode. - printStackTrace = undefined; jstestdriver.attachListener({ finish : function(tc, name, result, e) { diff --git a/polyfills/console.js b/polyfills/console.js new file mode 100644 index 0000000..4a6f542 --- /dev/null +++ b/polyfills/console.js @@ -0,0 +1,15 @@ +// Console-polyfill. MIT license. +// https://github.com/paulmillr/console-polyfill +// Make it safe to do console.log() always. +(function(con) { + 'use strict'; + var prop, method; + var empty = {}; + var dummy = function() {}; + var properties = 'memory'.split(','); + var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' + + 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' + + 'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(','); + while (prop = properties.pop()) con[prop] = con[prop] || empty; + while (method = methods.pop()) con[method] = con[method] || dummy; +})(this.console = this.console || {}); // Using `this` for web workers. diff --git a/push-to-web.sh b/push-to-web.sh index 232790e..785c4a1 100755 --- a/push-to-web.sh +++ b/push-to-web.sh @@ -30,9 +30,9 @@ if [ -s docs/options.html ] ; then find . -path ./.git -prune -o -print | xargs chmod a+rX # Copy everything to the site. - rsync -avzr gallery common tests jsdoc experimental plugins datahandler $site \ + rsync -avzr gallery common tests jsdoc experimental plugins datahandler polyfills $site \ && \ - rsync -avzr --copy-links dashed-canvas.js stacktrace.js dygraph*.js gadget.xml excanvas.js thumbnail.png screenshot.png $temp_dir/* $site/ + rsync -avzr --copy-links dashed-canvas.js dygraph*.js gadget.xml excanvas.js thumbnail.png screenshot.png $temp_dir/* $site/ else echo "generate-documentation.py failed" fi diff --git a/stacktrace.js b/stacktrace.js deleted file mode 100644 index 4d0384d..0000000 --- a/stacktrace.js +++ /dev/null @@ -1,411 +0,0 @@ -// Domain Public by Eric Wendelin http://eriwen.com/ (2008) -// Luke Smith http://lucassmith.name/ (2008) -// Loic Dachary (2008) -// Johan Euphrosine (2008) -// Oyvind Sean Kinsey http://kinsey.no/blog (2010) -// Victor Homyakov (2010) - -/** - * Main function giving a function stack trace with a forced or passed in Error - * - * @cfg {Error} e The error to create a stacktrace from (optional) - * @cfg {Boolean} guess If we should try to resolve the names of anonymous functions - * @return {Array} of Strings with functions, lines, files, and arguments where possible - */ -function printStackTrace(options) { - options = options || {guess: true}; - var ex = options.e || null, guess = !!options.guess; - var p = new printStackTrace.implementation(), result = p.run(ex); - return (guess) ? p.guessAnonymousFunctions(result) : result; -} - -printStackTrace.implementation = function() { -}; - -printStackTrace.implementation.prototype = { - /** - * @param {Error} ex The error to create a stacktrace from (optional) - * @param {String} mode Forced mode (optional, mostly for unit tests) - */ - run: function(ex, mode) { - ex = ex || this.createException(); - // examine exception properties w/o debugger - //for (var prop in ex) {alert("Ex['" + prop + "']=" + ex[prop]);} - mode = mode || this.mode(ex); - if (mode === 'other') { - return this.other(arguments.callee); - } else { - return this[mode](ex); - } - }, - - createException: function() { - try { - this.undef(); - } catch (e) { - return e; - } - }, - - /** - * Mode could differ for different exception, e.g. - * exceptions in Chrome may or may not have arguments or stack. - * - * @return {String} mode of operation for the exception - */ - mode: function(e) { - if (e['arguments'] && e.stack) { - return 'chrome'; - } else if (typeof e.message === 'string' && typeof window !== 'undefined' && window.opera) { - // e.message.indexOf("Backtrace:") > -1 -> opera - // !e.stacktrace -> opera - if (!e.stacktrace) { - return 'opera9'; // use e.message - } - // 'opera#sourceloc' in e -> opera9, opera10a - if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { - return 'opera9'; // use e.message - } - // e.stacktrace && !e.stack -> opera10a - if (!e.stack) { - return 'opera10a'; // use e.stacktrace - } - // e.stacktrace && e.stack -> opera10b - if (e.stacktrace.indexOf("called from line") < 0) { - return 'opera10b'; // use e.stacktrace, format differs from 'opera10a' - } - // e.stacktrace && e.stack -> opera11 - return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b' - } else if (e.stack) { - return 'firefox'; - } - return 'other'; - }, - - /** - * Given a context, function name, and callback function, overwrite it so that it calls - * printStackTrace() first with a callback and then runs the rest of the body. - * - * @param {Object} context of execution (e.g. window) - * @param {String} functionName to instrument - * @param {Function} function to call with a stack trace on invocation - */ - instrumentFunction: function(context, functionName, callback) { - context = context || window; - var original = context[functionName]; - context[functionName] = function instrumented() { - callback.call(this, printStackTrace().slice(4)); - return context[functionName]._instrumented.apply(this, arguments); - }; - context[functionName]._instrumented = original; - }, - - /** - * Given a context and function name of a function that has been - * instrumented, revert the function to it's original (non-instrumented) - * state. - * - * @param {Object} context of execution (e.g. window) - * @param {String} functionName to de-instrument - */ - deinstrumentFunction: function(context, functionName) { - if (context[functionName].constructor === Function && - context[functionName]._instrumented && - context[functionName]._instrumented.constructor === Function) { - context[functionName] = context[functionName]._instrumented; - } - }, - - /** - * Given an Error object, return a formatted Array based on Chrome's stack string. - * - * @param e - Error object to inspect - * @return Array of function calls, files and line numbers - */ - chrome: function(e) { - var stack = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, ''). - replace(/^\s+at\s+/gm, ''). - replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2'). - replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}()@$1').split('\n'); - stack.pop(); - return stack; - }, - - /** - * Given an Error object, return a formatted Array based on Firefox's stack string. - * - * @param e - Error object to inspect - * @return Array of function calls, files and line numbers - */ - firefox: function(e) { - return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n'); - }, - - opera11: function(e) { - // "Error thrown at line 42, column 12 in () in file://localhost/G:/js/stacktrace.js:\n" - // "Error thrown at line 42, column 12 in () in file://localhost/G:/js/stacktrace.js:\n" - // "called from line 7, column 4 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n" - // "called from line 15, column 3 in file://localhost/G:/js/test/functional/testcase1.html:\n" - var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/; - var lines = e.stacktrace.split('\n'), result = []; - - for (var i = 0, len = lines.length; i < len; i += 2) { - var match = lineRE.exec(lines[i]); - if (match) { - var location = match[4] + ':' + match[1] + ':' + match[2]; - var fnName = match[3] || "global code"; - fnName = fnName.replace(//, "$1").replace(//, ANON); - result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, '')); - } - } - - return result; - }, - - opera10b: function(e) { - // "([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + - // "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + - // "@file://localhost/G:/js/test/functional/testcase1.html:15" - var ANON = '{anonymous}', lineRE = /^(.*)@(.+):(\d+)$/; - var lines = e.stacktrace.split('\n'), result = []; - - for (var i = 0, len = lines.length; i < len; i++) { - var match = lineRE.exec(lines[i]); - if (match) { - var fnName = match[1]? (match[1] + '()') : "global code"; - result.push(fnName + '@' + match[2] + ':' + match[3]); - } - } - - return result; - }, - - /** - * Given an Error object, return a formatted Array based on Opera 10's stacktrace string. - * - * @param e - Error object to inspect - * @return Array of function calls, files and line numbers - */ - opera10a: function(e) { - // " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n" - // " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n" - var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; - var lines = e.stacktrace.split('\n'), result = []; - - for (var i = 0, len = lines.length; i < len; i += 2) { - var match = lineRE.exec(lines[i]); - if (match) { - var fnName = match[3] || ANON; - result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, '')); - } - } - - return result; - }, - - // Opera 7.x-9.2x only! - opera9: function(e) { - // " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n" - // " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" - var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i; - var lines = e.message.split('\n'), result = []; - - for (var i = 2, len = lines.length; i < len; i += 2) { - var match = lineRE.exec(lines[i]); - if (match) { - result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, '')); - } - } - - return result; - }, - - // Safari, IE, and others - other: function(curr) { - var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10; - while (curr && stack.length < maxStackSize) { - fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON; - args = Array.prototype.slice.call(curr['arguments'] || []); - stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')'; - curr = curr.caller; - } - return stack; - }, - - /** - * Given arguments array as a String, subsituting type names for non-string types. - * - * @param {Arguments} object - * @return {Array} of Strings with stringified arguments - */ - stringifyArguments: function(args) { - var result = []; - var slice = Array.prototype.slice; - for (var i = 0; i < args.length; ++i) { - var arg = args[i]; - if (arg === undefined) { - result[i] = 'undefined'; - } else if (arg === null) { - result[i] = 'null'; - } else if (arg.constructor) { - if (arg.constructor === Array) { - if (arg.length < 3) { - result[i] = '[' + this.stringifyArguments(arg) + ']'; - } else { - result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']'; - } - } else if (arg.constructor === Object) { - result[i] = '#object'; - } else if (arg.constructor === Function) { - result[i] = '#function'; - } else if (arg.constructor === String) { - result[i] = '"' + arg + '"'; - } else if (arg.constructor === Number) { - result[i] = arg; - } - } - } - return result.join(','); - }, - - sourceCache: {}, - - /** - * @return the text from a given URL - */ - ajax: function(url) { - var req = this.createXMLHTTPObject(); - if (req) { - try { - req.open('GET', url, false); - req.send(null); - return req.responseText; - } catch (e) { - } - } - return ''; - }, - - /** - * Try XHR methods in order and store XHR factory. - * - * @return XHR function or equivalent - */ - createXMLHTTPObject: function() { - var xmlhttp, XMLHttpFactories = [ - function() { - return new XMLHttpRequest(); - }, function() { - return new ActiveXObject('Msxml2.XMLHTTP'); - }, function() { - return new ActiveXObject('Msxml3.XMLHTTP'); - }, function() { - return new ActiveXObject('Microsoft.XMLHTTP'); - } - ]; - for (var i = 0; i < XMLHttpFactories.length; i++) { - try { - xmlhttp = XMLHttpFactories[i](); - // Use memoization to cache the factory - this.createXMLHTTPObject = XMLHttpFactories[i]; - return xmlhttp; - } catch (e) { - } - } - }, - - /** - * Given a URL, check if it is in the same domain (so we can get the source - * via Ajax). - * - * @param url source url - * @return False if we need a cross-domain request - */ - isSameDomain: function(url) { - return url.indexOf(location.hostname) !== -1; - }, - - /** - * Get source code from given URL if in the same domain. - * - * @param url JS source URL - * @return Array of source code lines - */ - getSource: function(url) { - // TODO reuse source from script tags? - if (!(url in this.sourceCache)) { - this.sourceCache[url] = this.ajax(url).split('\n'); - } - return this.sourceCache[url]; - }, - - guessAnonymousFunctions: function(stack) { - for (var i = 0; i < stack.length; ++i) { - var reStack = /\{anonymous\}\(.*\)@(.*)/, - reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/, - frame = stack[i], ref = reStack.exec(frame); - - if (ref) { - var m = reRef.exec(ref[1]), file = m[1], - lineno = m[2], charno = m[3] || 0; - if (file && this.isSameDomain(file) && lineno) { - var functionName = this.guessAnonymousFunction(file, lineno, charno); - stack[i] = frame.replace('{anonymous}', functionName); - } - } - } - return stack; - }, - - guessAnonymousFunction: function(url, lineNo, charNo) { - var ret; - try { - ret = this.findFunctionName(this.getSource(url), lineNo); - } catch (e) { - ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString(); - } - return ret; - }, - - findFunctionName: function(source, lineNo) { - // FIXME findFunctionName fails for compressed source - // (more than one function on the same line) - // TODO use captured args - // function {name}({args}) m[1]=name m[2]=args - var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/; - // {name} = function ({args}) TODO args capture - // /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/ - var reFunctionExpression = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function\b/; - // {name} = eval() - var reFunctionEvaluation = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(?:eval|new Function)\b/; - // Walk backwards in the source lines until we find - // the line which matches one of the patterns above - var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos; - for (var i = 0; i < maxLines; ++i) { - // lineNo is 1-based, source[] is 0-based - line = source[lineNo - i - 1]; - commentPos = line.indexOf('//'); - if (commentPos >= 0) { - line = line.substr(0, commentPos); - } - // TODO check other types of comments? Commented code may lead to false positive - if (line) { - code = line + code; - m = reFunctionExpression.exec(code); - if (m && m[1]) { - return m[1]; - } - m = reFunctionDeclaration.exec(code); - if (m && m[1]) { - //return m[1] + "(" + (m[2] || "") + ")"; - return m[1]; - } - m = reFunctionEvaluation.exec(code); - if (m && m[1]) { - return m[1]; - } - } - } - return '(?)'; - } -}; -- 2.7.4