stacktrace-js
Advanced tools
Comparing version 0.5.3 to 0.6.0
{ | ||
"name": "stacktrace.js", | ||
"version": "0.5.2", | ||
"version": "0.6.0", | ||
"main": "./stacktrace.js", | ||
@@ -5,0 +5,0 @@ "dependencies": {}, |
{ | ||
"name": "stacktrace.js", | ||
"version": "0.5.3", | ||
"main": "./stacktrace.js", | ||
"version": "0.6.0", | ||
"repo": "eriwen/javascript-stacktrace", | ||
"main": "stacktrace.js", | ||
"scripts": [ | ||
"stacktrace.js" | ||
], | ||
"dependencies": {} | ||
} |
@@ -5,3 +5,3 @@ { | ||
"author": "Eric Wendelin <me@eriwen.com> (http://eriwen.com)", | ||
"version": "0.5.3", | ||
"version": "0.6.0", | ||
"keywords": ["stack-trace", "cross-browser", "framework-agnostic", "client", "browser"], | ||
@@ -8,0 +8,0 @@ "homepage": "http://stacktracejs.com", |
@@ -1,3 +0,3 @@ | ||
# What is stacktrace.js? # | ||
A Javascript tool that allows you to debug your JavaScript by giving you a [stack trace](http://en.wikipedia.org/wiki/Stack_trace) of function calls leading to an error (or any condition you specify) | ||
# Welcome to stacktrace.js! [![Code Climate](https://codeclimate.com/github/eriwen/javascript-stacktrace.png)](https://codeclimate.com/github/eriwen/javascript-stacktrace) | ||
A JavaScript tool that allows you to debug your JavaScript by giving you a [stack trace](http://en.wikipedia.org/wiki/Stack_trace) of function calls leading to an error (or any condition you specify) | ||
@@ -8,31 +8,30 @@ # How do I use stacktrace.js? # | ||
```html | ||
<script type="text/javascript" src="path/to/stacktrace.js" /> | ||
<script type="text/javascript" src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script> | ||
<script type="text/javascript"> | ||
... your code ... | ||
if (errorCondition) { | ||
var trace = printStackTrace(); | ||
//Output however you want! | ||
alert(trace.join('\n\n')); | ||
} | ||
... more code of yours ... | ||
// your code... | ||
var trace = printStackTrace(); | ||
alert(trace.join('\n\n')); // Output however you want! | ||
// more code of yours... | ||
</script> | ||
``` | ||
Bookmarklet available on the [project home page](http://stacktracejs.com). | ||
You can also pass in your own Error to get a stacktrace *not available in IE or Safari 5-* | ||
```javascript | ||
var lastError; | ||
try { | ||
// error producing code | ||
} catch(e) { | ||
lastError = e; | ||
// do something else with error | ||
} | ||
// Returns stacktrace from lastError! | ||
printStackTrace({e: lastError}); | ||
```html | ||
<script type="text/javascript" src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script> | ||
<script type="text/javascript"> | ||
try { | ||
// error producing code | ||
} catch(e) { | ||
var trace = printStackTrace({e: e}); | ||
alert('Error!\n' + 'Message: ' + e.message + '\nStack trace:\n' + trace.join('\n')); | ||
// do something else with error | ||
} | ||
</script> | ||
``` | ||
Note that error message is not included in stack trace. | ||
Bookmarklet available on the [project home page](http://stacktracejs.com). | ||
# Function Instrumentation # | ||
@@ -42,7 +41,8 @@ You can now have any (public or privileged) function give you a stacktrace when it is called: | ||
```javascript | ||
var p = new printStackTrace.implementation(); | ||
p.instrumentFunction(this, 'baz', logStackTrace); | ||
function logStackTrace(stack) { | ||
console.log(stack.join('\n')); | ||
} | ||
var p = new printStackTrace.implementation(); | ||
p.instrumentFunction(this, 'baz', logStackTrace); | ||
function foo() { | ||
@@ -75,3 +75,4 @@ var a = 1; | ||
## Contributions | ||
## Contributions [![Stories in Ready](http://badge.waffle.io/eriwen/javascript-stacktrace.png)](http://waffle.io/eriwen/javascript-stacktrace) | ||
This project is made possible due to the efforts of these fine people: | ||
@@ -78,0 +79,0 @@ |
@@ -7,445 +7,480 @@ // Domain Public by Eric Wendelin http://eriwen.com/ (2008) | ||
// Victor Homyakov <victor-homyakov@users.sourceforge.net> (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; | ||
} | ||
/** | ||
* 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() { | ||
}; | ||
if (typeof module !== "undefined" && module.exports) { | ||
module.exports = printStackTrace; | ||
} | ||
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); | ||
} | ||
}, | ||
printStackTrace.implementation = function() { | ||
}; | ||
createException: function() { | ||
try { | ||
this.undef(); | ||
} catch (e) { | ||
return e; | ||
} | ||
}, | ||
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); | ||
} | ||
}, | ||
/** | ||
* 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'; | ||
} | ||
createException: function() { | ||
try { | ||
this.undef(); | ||
} catch (e) { | ||
return e; | ||
} | ||
}, | ||
if (e.stack && e.sourceURL) { | ||
return 'safari'; | ||
} | ||
/** | ||
* 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 (e.stack && e.sourceURL) { | ||
return 'safari'; | ||
} else if (e.stack && e.number) { | ||
return 'ie'; | ||
} 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 | ||
if (e.stack && e.number) { | ||
return 'ie'; | ||
} | ||
// '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 | ||
if (e.stack && e.fileName) { | ||
return 'firefox'; | ||
} | ||
// e.stacktrace && !e.stack -> opera10a | ||
if (!e.stack) { | ||
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 | ||
} | ||
// e.stacktrace && e.stack -> opera10b | ||
if (e.stacktrace.indexOf("called from line") < 0) { | ||
return 'opera10b'; // use e.stacktrace, format differs from 'opera10a' | ||
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' | ||
} | ||
// e.stacktrace && e.stack -> opera11 | ||
return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b' | ||
} else 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'; | ||
} 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} 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; | ||
}, | ||
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'; | ||
} | ||
/** | ||
* 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 && | ||
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; | ||
} | ||
}, | ||
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<String> of function calls, files and line numbers | ||
*/ | ||
chrome: function(e) { | ||
var stack = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, ''). | ||
replace(/^\s+(at eval )?at\s+/gm, ''). | ||
replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2'). | ||
replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}()@$1').split('\n'); | ||
stack.pop(); | ||
return stack; | ||
}, | ||
/** | ||
* Given an Error object, return a formatted Array based on Chrome's stack string. | ||
* | ||
* @param e - Error object to inspect | ||
* @return Array<String> 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.<anonymous>\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<String> 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 Safari's stack string. | ||
* | ||
* @param e - Error object to inspect | ||
* @return Array<String> 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<String> of function calls, files and line numbers | ||
*/ | ||
ie: function(e) { | ||
var lineRE = /^.*at (\w+) \(([^\)]+)\)$/gm; | ||
return e.stack.replace(/at Anonymous function /gm, '{anonymous}()@') | ||
.replace(/^(?=\w+Error\:).*$\n/m, '') | ||
.replace(lineRE, '$1@$2') | ||
.split('\n'); | ||
}, | ||
/** | ||
* Given an Error object, return a formatted Array based on IE's stack string. | ||
* | ||
* @param e - Error object to inspect | ||
* @return Array<String> 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<String> of function calls, files and line numbers | ||
*/ | ||
firefox: function(e) { | ||
return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^[\(@]/gm, '{anonymous}()@').split('\n'); | ||
}, | ||
/** | ||
* Given an Error object, return a formatted Array based on Firefox's stack string. | ||
* | ||
* @param e - Error object to inspect | ||
* @return Array<String> 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 = []; | ||
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(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON); | ||
result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, '')); | ||
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(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON); | ||
result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, '')); | ||
} | ||
} | ||
} | ||
return result; | ||
}, | ||
return result; | ||
}, | ||
opera10b: function(e) { | ||
// "<anonymous function: run>([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 = []; | ||
opera10b: function(e) { | ||
// "<anonymous function: run>([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]); | ||
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; | ||
}, | ||
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<String> 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 = []; | ||
/** | ||
* Given an Error object, return a formatted Array based on Opera 10's stacktrace string. | ||
* | ||
* @param e - Error object to inspect | ||
* @return Array<String> 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+/, '')); | ||
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; | ||
}, | ||
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 = []; | ||
// 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+/, '')); | ||
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; | ||
}, | ||
return result; | ||
}, | ||
// Safari 5-, IE 9-, and others | ||
other: function(curr) { | ||
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10; | ||
while (curr && curr['arguments'] && 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; | ||
}, | ||
// Safari 5-, IE 9-, and others | ||
other: function(curr) { | ||
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10; | ||
var slice = Array.prototype.slice; | ||
while (curr && curr['arguments'] && stack.length < maxStackSize) { | ||
fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON; | ||
args = slice.call(curr['arguments'] || []); | ||
stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')'; | ||
try { | ||
curr = curr.caller; | ||
} catch (e) { | ||
stack[stack.length] = '' + 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) { | ||
if (arg.constructor === Array) { | ||
if (arg.length < 3) { | ||
result[i] = '[' + this.stringifyArguments(arg) + ']'; | ||
/** | ||
* 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] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']'; | ||
result[i] = '?'; | ||
} | ||
} 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(','); | ||
}, | ||
return result.join(','); | ||
}, | ||
sourceCache: {}, | ||
sourceCache: {}, | ||
/** | ||
* @return 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 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 ''; | ||
}, | ||
return ''; | ||
}, | ||
/** | ||
* Try XHR methods in order and store XHR factory. | ||
* | ||
* @return <Function> 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'); | ||
/** | ||
* Try XHR methods in order and store XHR factory. | ||
* | ||
* @return <Function> 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) { | ||
} | ||
} | ||
]; | ||
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. | ||
}, | ||
/** | ||
* 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]; | ||
}, | ||
/** | ||
* 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); | ||
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); | ||
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; | ||
}, | ||
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; | ||
}, | ||
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]; | ||
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); | ||
} | ||
m = reFunctionDeclaration.exec(code); | ||
if (m && m[1]) { | ||
//return m[1] + "(" + (m[2] || "") + ")"; | ||
return m[1]; | ||
// 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]; | ||
} | ||
} | ||
m = reFunctionEvaluation.exec(code); | ||
if (m && m[1]) { | ||
return m[1]; | ||
} | ||
} | ||
return '(?)'; | ||
} | ||
return '(?)'; | ||
} | ||
}; | ||
}; | ||
return printStackTrace; | ||
})); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29964
10
465
83