current-executing-script
Version:
Get the currently executing script, regardless of its source/trigger/synchronicity. Similar to HTML5's `document.currentScript` but arguably much more useful!
133 lines • 7.55 kB
JavaScript
/*!
* currentExecutingScript
* Get the currently executing script, regardless of its source/trigger/synchronicity. Similar to HTML5's `document.currentScript` but arguably much more useful!
* Copyright (c) 2015 James M. Greene
* Licensed MIT
* https://github.com/JamesMGreene/currentExecutingScript
* v0.1.3
*/
!function(a,b){"function"==typeof define&&define.amd?
// AMD. Register as an anonymous module.
define([],b):"object"==typeof exports?module.exports=b():a.currentExecutingScript=b()}(
// Current context/scope
this||window,
// Factory function to return the export
function(){
// Normalize whitespace within a string
function a(a){return a?a.replace(/^\s+$|\s+$/g,"").replace(/\s\s+/g," "):""}
// Get script object based on the `src` URL
function b(a,b){var c,d=null;if(b=b||m,"string"==typeof a&&a)for(c=b.length;c--;)if(b[c].src===a){
// NOTE: Could check if the same script URL is used by more than one `script` element
// here... but let's not. That would yield less useful results in "loose" detection. ;)
d=b[c];break}return d}
// Get script object based on the caller function's source code body (text)
function c(b,c){var d,e,f=null,g=a(b);if(c=c||m,b&&g)for(d=c.length;d--;)
// Only look at inline scripts
if(!c[d].hasAttribute("src")&&(e=a(c[d].text),-1!==e.indexOf(g))){
// If more than one match is found, don't return any
if(f){f=null;break}f=c[d]}return f}
// If there is only a single inline script on the page, return it; otherwise `null`
function d(a){var b,c,d=null;for(a=a||m,b=0,c=a.length;c>b;b++)if(!a[b].hasAttribute("src")){if(d){d=null;break}d=a[b]}return d}
// Get the currently executing script URL from an Error stack trace
function e(a,b){var c,d,f=null,g="number"==typeof b;return b=g?Math.round(b):0,"string"==typeof a&&a&&(g?c=a.match(/(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/):(c=a.match(/^(?:|[^:@]*@|.+\)@(?=data:text\/javascript|blob|http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]||(c=a.match(/\)@(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/))),c&&c[1]&&(b>0?(d=a.slice(a.indexOf(c[0])+c[0].length),f=e(d,b-1)):f=c[1])),f}
// Get the farthest currently executing (i.e. yes, EXECUTING) `script` DOM
// element for the caller function, regardless of whether it is that `script`
// DOM element is currently being evaluated for the first time. The farthest
// currently executing `script` DOM element would typically be considered the
// originator of the current execution stack.
function f(){/*jshint noarg:false */
// TODO: Implement!
// Fixes #3
// See https://github.com/JamesMGreene/currentExecutingScript/issues/3
return null}
// Get the originating currently executing (i.e. yes, EXECUTING) `script` DOM
// element or attribute node (e.g. `onclick`) for the caller function,
// regardless of whether it is that `script` DOM element is currently being
// evaluated for the first time. The originating currently executing `script`
// DOM element [or attribute node] is the originator of the current execution stack.
function g(){
// TODO: Implement!
// Fixes #2
// See https://github.com/JamesMGreene/currentExecutingScript/issues/2
return null}
// Get the nearest currently executing (i.e. yes, EXECUTING) `script` DOM
// element for the caller function, regardless of whether it is that `script`
// DOM element is currently being evaluated for the first time.
function h(){/*jshint noarg:false */
// Yes, this IS possible, i.e. if a script removes other scripts (or itself)
if(0===m.length)return null;var a,f,g,i,k,s=[],t=h.skipStackDepth||1,
// TODO: Implement!
// Fixes #4 in part
// See https://github.com/JamesMGreene/currentExecutingScript/issues/4
u=null;//("" + (_nearestExecutingScript.caller || "")) || null;
// This part will only help in IE 6-10.
for(a=0;a<m.length;a++)o&&n?j.test(m[a].readyState)&&s.push(m[a]):s.push(m[a]);if(f=new Error,q&&(g=f.stack),!g&&r)try{throw f}catch(v){
// NOTE: Cannot use `err.sourceURL` or `err.fileName` as they will always be THIS script
g=v.stack}if(g&&(i=e(g,t),k=b(i,s),!k&&l&&i===l&&(k=u?c(u,s):d(s))),
//
// Welcome to the Island of Inaccurate Assumptions!
// NOTE: ALL of the following are loose assumptions that could be inaccurate!
//
k||
// Inaccuracies:
// - If the inline script that initiated the call was also removed from the DOM.
// - If the call was initiated by an element's inline event handler,
// e.g. `<a onclick="(function() { alert(currentExecutingScript()); }()">click</a>`
1===s.length&&(k=s[0]),k||
// Inaccuracies:
// - If script currently being synchronously evaluated by the parser is the
// originator of this call stack but NOT the source script of the caller/invocation
// e.g.
// ```html
// <script id="a">
// function getCurrentScriptCallerFn() {
// return currentExecutingScript.near();
// }
// </script>
// <script id="b">
// // Should get `script[id="a"]` but will get `script[id="b"]` instead
// getCurrentScriptCallerFn();
// </script>
p&&(k=document.currentScript),!k&&o&&n)for(a=s.length;a--;)if("interactive"===s[a].readyState){k=s[a];break}
// Inaccuracies:
// - If a script is created dynamically and appended to some position
// other than the very end of the document.
// - If multiple scripts are created dynamically and all appended to the
// same position within the document (and do not have their `async` attributes
// set to `false`, at least in browsers that support async script evaluation.
// other than the very end of the document.
// - If any scripts are added with the `async` attribute set to `true` in a browser
// that supports it.
// - May get confused by `script` elements within `svg` elements
// - If script currently being synchronously evaluated by the parser is the
// originator of this call stack but NOT the source script of the caller/invocation
// e.g.
// ```html
// <script id="a">
// function getCurrentScriptCallerFn() {
// return currentExecutingScript.near();
// }
// </script>
// <script id="b">
// // Should get `script[id="a"]` but will get `script[id="b"]` instead
// getCurrentScriptCallerFn();
// </script>
// ```
return k||(k=s[s.length-1]||null),k}var i,j=/^(interactive|loaded|complete)$/,k=window.location?window.location.href:null,l=k?k.replace(/#.*$/,"").replace(/\?.*$/,"")||null:null,m=document.getElementsByTagName("script"),n="readyState"in(m[0]||document.createElement("script")),o=!window.opera||"[object Opera]"!==window.opera.toString(),p="currentScript"in document;
// Detect if the V8 Error Stack Trace API is supported
"stackTraceLimit"in Error&&Error.stackTraceLimit!==1/0&&(i=Error.stackTraceLimit,Error.stackTraceLimit=1/0);
// In some browsers (e.g. Chrome), you can get the current stack from an Error
// object instance without needing to throw it. Avoiding an unnecessary
// use of `throw` saves time and performance.
var q=!1,r=!1;!function(){try{var a=new Error;throw q="string"==typeof a.stack&&!!a.stack,a}catch(b){r="string"==typeof b.stack&&!!b.stack}}(),
// Default stack depth to skip over when analyzing call stack frames
h.skipStackDepth=1;
//
// Export the API
//
var s=h;
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
// default
return s.near=h,s.far=f,s.origin=g,s});