diff --git a/ajax/libs/stacktrace.js/0.6.2/stacktrace.js b/ajax/libs/stacktrace.js/0.6.2/stacktrace.js new file mode 100644 index 000000000..5ddfd7202 --- /dev/null +++ b/ajax/libs/stacktrace.js/0.6.2/stacktrace.js @@ -0,0 +1,489 @@ +// Domain Public by Eric Wendelin http://www.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) +/*global module, exports, define, ActiveXObject*/ +(function(global, factory) { + if (typeof exports === 'object') { + // Node + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + // AMD + define(factory); + } else { + // Browser globals + global.printStackTrace = factory(); + } +}(this, function() { + /** + * 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(); + 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'; + } + + if (e.stack && e.sourceURL) { + return 'safari'; + } + + if (e.stack && e.number) { + return 'ie'; + } + + if (e.stack && e.fileName) { + return 'firefox'; + } + + if (e.message && e['opera#sourceloc']) { + // e.message.indexOf("Backtrace:") > -1 -> opera9 + // 'opera#sourceloc' in e -> opera9, opera10a + // !e.stacktrace -> opera9 + if (!e.stacktrace) { + return 'opera9'; // use e.message + } + if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { + // e.message may have more stack entries than e.stacktrace + return 'opera9'; // use e.message + } + return 'opera10a'; // use e.stacktrace + } + + if (e.message && e.stack && 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' + } + + if (e.stack && !e.fileName) { + // Chrome 27 does not have e.arguments as earlier versions, + // but still does not have e.fileName as Firefox + return 'chrome'; + } + + 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} callback 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) { + return (e.stack + '\n') + .replace(/^[\s\S]+?\s+at\s+/, ' at ') // remove message + .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation + .replace(/^([^\(]+?)([\n$])/gm, '{anonymous}() ($1)$2') + .replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}() ($1)') + .replace(/^(.+) \((.+)\)$/gm, '$1@$2') + .split('\n') + .slice(0, -1); + }, + + /** + * Given an Error object, return a formatted Array based on Safari's stack string. + * + * @param e - Error object to inspect + * @return Array of function calls, files and line numbers + */ + safari: function(e) { + return e.stack.replace(/\[native code\]\n/m, '') + .replace(/^(?=\w+Error\:).*$\n/m, '') + .replace(/^@/gm, '{anonymous}()@') + .split('\n'); + }, + + /** + * Given an Error object, return a formatted Array based on IE's stack string. + * + * @param e - Error object to inspect + * @return Array of function calls, files and line numbers + */ + ie: function(e) { + return e.stack + .replace(/^\s*at\s+(.*)$/gm, '$1') + .replace(/^Anonymous function\s+/gm, '{anonymous}() ') + .replace(/^(.+)\s+\((.+)\)$/gm, '$1@$2') + .split('\n') + .slice(1); + }, + + /** + * 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(/^(?:\((\S*)\))?@/gm, '{anonymous}($1)@') + .split('\n'); + }, + + opera11: function(e) { + 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 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 5-, IE 9-, and others + other: function(curr) { + var ANON = '{anonymous}', fnRE = /function(?:\s+([\w$]+))?\s*\(/, stack = [], fn, args, maxStackSize = 10; + var slice = Array.prototype.slice; + while (curr && stack.length < maxStackSize) { + fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON; + try { + args = slice.call(curr['arguments'] || []); + } catch (e) { + args = ['Cannot access arguments: ' + e]; + } + stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')'; + try { + curr = curr.caller; + } catch (e) { + stack[stack.length] = 'Cannot access caller: ' + e; + break; + } + } + return stack; + }, + + /** + * Given arguments array as a String, substituting type names for non-string types. + * + * @param {Arguments,Array} args + * @return {String} 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) { + // TODO constructor comparison does not work for iframes + 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; + } else { + result[i] = '?'; + } + } + } + return result.join(','); + }, + + sourceCache: {}, + + /** + * @return {String} the text from a given URL + */ + ajax: function(url) { + var req = this.createXMLHTTPObject(); + if (req) { + try { + req.open('GET', url, false); + //req.overrideMimeType('text/plain'); + //req.overrideMimeType('text/javascript'); + req.send(null); + //return req.status == 200 ? req.responseText : ''; + return req.responseText; + } catch (e) { + } + } + return ''; + }, + + /** + * Try XHR methods in order and store XHR factory. + * + * @return {XMLHttpRequest} 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 {String} source url + * @return {Boolean} False if we need a cross-domain request + */ + isSameDomain: function(url) { + return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs. + }, + + /** + * Get source code from given URL if in the same domain. + * + * @param url {String} JS source URL + * @return {Array} 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]); + if (m) { // If falsey, we did not get any file/line information + var 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) + // 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 = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/; + // {name} = eval() + var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\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 '(?)'; + } + }; + + return printStackTrace; +})); diff --git a/ajax/libs/stacktrace.js/0.6.2/stacktrace.min.js b/ajax/libs/stacktrace.js/0.6.2/stacktrace.min.js new file mode 100644 index 000000000..b14b357cb --- /dev/null +++ b/ajax/libs/stacktrace.js/0.6.2/stacktrace.min.js @@ -0,0 +1 @@ +!function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.printStackTrace=b()}(this,function(){function a(b){b=b||{guess:!0};var c=b.e||null,d=!!b.guess,e=new a.implementation,f=e.run(c);return d?e.guessAnonymousFunctions(f):f}return a.implementation=function(){},a.implementation.prototype={run:function(a,b){return a=a||this.createException(),b=b||this.mode(a),"other"===b?this.other(arguments.callee):this[b](a)},createException:function(){try{this.undef()}catch(a){return a}},mode:function(a){return a.arguments&&a.stack?"chrome":a.stack&&a.sourceURL?"safari":a.stack&&a.number?"ie":a.stack&&a.fileName?"firefox":a.message&&a["opera#sourceloc"]?a.stacktrace?a.message.indexOf("\n")>-1&&a.message.split("\n").length>a.stacktrace.split("\n").length?"opera9":"opera10a":"opera9":a.message&&a.stack&&a.stacktrace?a.stacktrace.indexOf("called from line")<0?"opera10b":"opera11":a.stack&&!a.fileName?"chrome":"other"},instrumentFunction:function(b,c,d){b=b||window;var e=b[c];b[c]=function(){return d.call(this,a().slice(4)),b[c]._instrumented.apply(this,arguments)},b[c]._instrumented=e},deinstrumentFunction:function(a,b){a[b].constructor===Function&&a[b]._instrumented&&a[b]._instrumented.constructor===Function&&(a[b]=a[b]._instrumented)},chrome:function(a){return(a.stack+"\n").replace(/^[\s\S]+?\s+at\s+/," at ").replace(/^\s+(at eval )?at\s+/gm,"").replace(/^([^\(]+?)([\n$])/gm,"{anonymous}() ($1)$2").replace(/^Object.\s*\(([^\)]+)\)/gm,"{anonymous}() ($1)").replace(/^(.+) \((.+)\)$/gm,"$1@$2").split("\n").slice(0,-1)},safari:function(a){return a.stack.replace(/\[native code\]\n/m,"").replace(/^(?=\w+Error\:).*$\n/m,"").replace(/^@/gm,"{anonymous}()@").split("\n")},ie:function(a){return a.stack.replace(/^\s*at\s+(.*)$/gm,"$1").replace(/^Anonymous function\s+/gm,"{anonymous}() ").replace(/^(.+)\s+\((.+)\)$/gm,"$1@$2").split("\n").slice(1)},firefox:function(a){return a.stack.replace(/(?:\n@:0)?\s+$/m,"").replace(/^(?:\((\S*)\))?@/gm,"{anonymous}($1)@").split("\n")},opera11:function(a){for(var b="{anonymous}",c=/^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/,d=a.stacktrace.split("\n"),e=[],f=0,g=d.length;g>f;f+=2){var h=c.exec(d[f]);if(h){var i=h[4]+":"+h[1]+":"+h[2],j=h[3]||"global code";j=j.replace(//,"$1").replace(//,b),e.push(j+"@"+i+" -- "+d[f+1].replace(/^\s+/,""))}}return e},opera10b:function(a){for(var b=/^(.*)@(.+):(\d+)$/,c=a.stacktrace.split("\n"),d=[],e=0,f=c.length;f>e;e++){var g=b.exec(c[e]);if(g){var h=g[1]?g[1]+"()":"global code";d.push(h+"@"+g[2]+":"+g[3])}}return d},opera10a:function(a){for(var b="{anonymous}",c=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,d=a.stacktrace.split("\n"),e=[],f=0,g=d.length;g>f;f+=2){var h=c.exec(d[f]);if(h){var i=h[3]||b;e.push(i+"()@"+h[2]+":"+h[1]+" -- "+d[f+1].replace(/^\s+/,""))}}return e},opera9:function(a){for(var b="{anonymous}",c=/Line (\d+).*script (?:in )?(\S+)/i,d=a.message.split("\n"),e=[],f=2,g=d.length;g>f;f+=2){var h=c.exec(d[f]);h&&e.push(b+"()@"+h[2]+":"+h[1]+" -- "+d[f+1].replace(/^\s+/,""))}return e},other:function(a){for(var e,f,b="{anonymous}",c=/function(?:\s+([\w$]+))?\s*\(/,d=[],g=10,h=Array.prototype.slice;a&&d.lengthk;++k)if(g=a[b-k-1],j=g.indexOf("//"),j>=0&&(g=g.substr(0,j)),g){if(f=g+f,i=d.exec(f),i&&i[1])return i[1];if(i=c.exec(f),i&&i[1])return i[1];if(i=e.exec(f),i&&i[1])return i[1]}return"(?)"}},a}); diff --git a/ajax/libs/stacktrace.js/package.json b/ajax/libs/stacktrace.js/package.json index 10263ad0f..72d7acb0e 100644 --- a/ajax/libs/stacktrace.js/package.json +++ b/ajax/libs/stacktrace.js/package.json @@ -2,7 +2,7 @@ "name": "stacktrace.js", "filename": "stacktrace.min.js", "description": "Framework-agnostic, micro-library for getting stack traces in all environments", - "version": "0.6.0", + "version": "0.6.2", "keywords": ["stack-trace", "cross-browser", "framework-agnostic", "client", "browser"], "homepage": "http://stacktracejs.com", "maintainers": [