UNPKG

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
/*! * 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});