How might I get the script filename from within that script? - javascript

I'm pretty sure the answer is no, but thought I'd ask anyway.
If my site references a scripted named "whatever.js", is it possible to get "whatever.js" from within that script? Like:
var scriptName = ???
if (typeof jQuery !== "function") {
throw new Error(
"jQuery's script needs to be loaded before " +
scriptName + ". Check the <script> tag order.");
}
Probably more trouble than it's worth for dependency checking, but what the hell.

var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length-1];
var scriptName = lastScript.src;
alert("loading: " + scriptName);
Tested in: FF 3.0.8, Chrome 1.0.154.53, IE6
See also: How may I reference the script tag that loaded the currently-executing script?

I'm aware this is old but I have developed a better solution because all of the above didn't work for Async scripts. With some tweaking the following script can cover almost all use cases. Heres what worked for me:
function getScriptName() {
var error = new Error()
, source
, lastStackFrameRegex = new RegExp(/.+\/(.*?):\d+(:\d+)*$/)
, currentStackFrameRegex = new RegExp(/getScriptName \(.+\/(.*):\d+:\d+\)/);
if((source = lastStackFrameRegex.exec(error.stack.trim())) && source[1] != "")
return source[1];
else if((source = currentStackFrameRegex.exec(error.stack.trim())))
return source[1];
else if(error.fileName != undefined)
return error.fileName;
}
Not sure about support on Internet Explorer, but works fine in every other browser I tested on.

You can use...
var scripts = document.getElementsByTagName("script"),
currentScriptUrl = (document.currentScript || scripts[scripts.length - 1]).src;
currentScript() is supported by all browsers except IE.
Make sure it's ran as the file is parsed and executed, not on DOM ready or window load.
If it's an empty string, your script block has no or an empty src attribute.

In Node.js:
var abc = __filename.split(__dirname+"/").pop();

Shog9's suggestion more shorter:
alert("loading: " + document.scripts[document.scripts.length-1].src);

You can return a list of script elements in the page:
var scripts = document.getElementsByTagName("script");
And then evaluate each one and retrieve its location:
var location;
for(var i=0; i<scripts.length;++i) {
location = scripts[i].src;
//Do stuff with the script location here
}

As the "src" attribute holds the full path to the script file you can add a substring call to get the file name only.
var path = document.scripts[document.scripts.length-1].src;
var fileName = path.substring(path.lastIndexOf('/')+1);

I had issues with the above code while extracting the script name when the calling code is included inside a .html file.
Hence I developed this solution:
var scripts = document.getElementsByTagName( "script" ) ;
var currentScriptUrl = ( document.currentScript || scripts[scripts.length - 1] ).src ;
var scriptName = currentScriptUrl.length > 0 ? currentScriptUrl : scripts[scripts.length-1].baseURI.split( "/" ).pop() ;

You can try putting this at the top of your JavaScript file:
window.myJSFilename = "";
window.onerror = function(message, url, line) {
if (window.myJSFilename != "") return;
window.myJSFilename = url;
}
throw 1;
Make sure you have only functions below this. The myJSFilename variable will contain the full path of the JavaScript file, the filename can be parsed from that. Tested in IE11, but it should work elsewhere.

If you did't want use jQuery:
function getCurrentFile() {
var filename = document.location.href;
var tail = (filename.indexOf(".", (filename.indexOf(".org") + 1)) == -1) ? filename.length : filename.lastIndexOf(".");
return (filename.lastIndexOf("/") >= (filename.length - 1)) ? (filename.substring(filename.substring(0, filename.length - 2).lastIndexOf("/") + 1, filename.lastIndexOf("/"))).toLowerCase() : (filename.substring(filename.lastIndexOf("/") + 1, tail)).toLowerCase();
}

What will happen if the jQuery script isn't there? Are you just going to output a message? I guess it is slightly better for debugging if something goes wrong, but it's not very helpful for users.
I'd say just design your pages such that this occurrence will not happen, and in the rare event it does, just let the script fail.

The only way that is waterproof:
var code = this.__proto__.constructor.toString();
$("script").each(function (index, element) {
var src = $(element).attr("src");
if (src !== undefined) {
$.get(src, function (data) {
if (data.trim() === code) {
scriptdir = src.substring(0, src.lastIndexOf("/"));
}
});
}
});
"var code" can also be the function name, but with the prototype constructor you don't have to modify anything. This code compares its own content against all present scripts. No hard coded filenames needed anymore.
Korporal Nobbs was in the right direction, but not complete with the comparison.

Related

Whats the appropriate method of acquiring consistent response for innerHTML data using $.get across different browser platforms?

Given the following code:
var hostUri = window.location.origin + '/GuestValidator.asmx/GetReservationInfo?resvNo=' + reservationno;
$.get(hostUri, {}, function (response) {
var data = response.documentElement.innerHTML;
}
If executed within a Chrome browser, the expected result returns a comma-delimited string of values within the innerHTML property of the documentElement object.
However, if the same code is executed using IE 11 or Edge there is no innerHTML property on the documentElement. It comes back as undefined.
But I can revise my code to response.documentElement.childNodes[0].data when executed within IE and get the same result that was present in Chrome using innerHTML.
I haven't tested the code with other browsers but I suspect they will likely have differences also.
So how can this code be revised to acquire what would be the innerHTML value within Chrome across all browsers without having to execute separate code based on the browser?
EDITED FOR CLARITY
This is an excerpt of the web method. It is returning a generic list of type string. It doesn't matter whether or not I return a JSON object.
var arry = new List<string>();
var blockedOrderDays = 3;
var blockedDay = String.Empty;
var noOrderDays = String.Empty;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var svclient = new ServiceClient();
var result = svclient.GetReservation(reservationNo);
var details = result.Reservation.ArrivalDate;
var arrivalDt = formatDate(details);
while(blockedOrderDays != 0)
{
blockedDay = formatDate(details.AddDays(-blockedOrderDays));
arry.Add(blockedDay);
noOrderDays += blockedDay + ",";
-- blockedOrderDays;
++x;
}
arry.Add(arrivalDt);
noOrderDays = noOrderDays + arrivalDt;
return noOrderDays;
After spending some additional time and effort on getting this code to work with a reasonable level of continuity across browser platforms. The following modification appears to have resolved the issue.
var hostUri = window.location.origin + '/GuestValidator.asmx/GetReservationInfo?resvNo=' + reservationno;
$.get(hostUri, {}, function (response) {
if ((navigator.userAgent.indexOf("MSIE") !== -1) || (!!document.documentMode === true))
prohibitOrderDates = response.childNodes[0].textContent;
else
prohibitOrderDates = response.children[0].innerHTML;
var data = response.children[0].innerHTML;
}
Alternatively, though not specific to the answer. A shortened version of the call can be:
var hostUri = window.location.origin + '/GuestValidator.asmx/GetReservationInfo';
$.get(hostUri, { resvNo=reservationno }, function (response) {
if ((navigator.userAgent.indexOf("MSIE") !== -1) || (!!document.documentMode === true))
prohibitOrderDates = response.childNodes[0].textContent;
else
prohibitOrderDates = response.children[0].innerHTML;
var data = response.children[0].innerHTML;
}
So the response.children[0].innerHTML is the appropriate call to retain browser continuity for most every browser I've tested and IE always seems to be the exception to the rule. Even Microsoft Edge follows the standard. IE seems to be an odd ball for ever keeping standard.

IE9 throws DOM Exception: INVALID_CHARACTER_ERR (5)

On the script below, IE9 is throwing an error:
SCRIPT5022: DOM Exception: INVALID_CHARACTER_ERR (5)
mootools-1.2.1-core-yc.js, line 118 character 1
Document.implement({
newElement: function (A, B) {
if (Browser.Engine.trident && B) {
["name", "type", "checked"].each(function (C) {
if (!B[C]) {
return;
}
A += " " + C + '="' + B[C] + '"';
if (C != "checked") {
delete B[C];
}
});
A = "<" + A + ">";
}
return $.element(this.createElement(A)).set(B); //THIS IS LINE 118
},
newTextNode: function (A) {
return this.createTextNode(A);
},
getDocument: function () {
return this;
},
getWindow: function () {
return this.window;
}
});
This snippet is part of the Mootools js library that the developer used on the site. Is there a workaround to fix the error for IE?
yeah that code is garbage, you should never do browser checks like that, its taught in JavaScript 101... lol can't believe that's in mootools? blech, anyways
IE9 doesn't allow for crazy document.createElement('<div style="background:red">yay!</div>'); syntax anymore (no one should've ever really been using it in the first place...)
here's an example:
var d = document;
var x = d.createElement('div');
x.innerHTML = 'yay';
x.style.backgroundColor = 'red';
x.style.padding = '6px';
x.style.margin = '20px';
d.body.appendChild(x);
var sameHTML = '<div style="background:green;padding:6px;margin:20px;">yay!</div>';
// fails in IE > 8 and other browsers
try {
var y = d.createElement(sameHTML);
d.body.appendChild(y);
} catch (err) {
d.body.appendChild(d.createTextNode(err));
}
// quick fix using innerHTML:
var temp = d.createElement('div');
temp.innerHTML = sameHTML;
d.body.appendChild(temp.childNodes[0]);
the way to fix this is to either create a dummy element and use .innerHTML and then extract the child, or inside mootools check the browser version and don't do that for IE > 8 if i remember right mootools has a Browser.Engine.version or something to that effect...
edit: i feel like i should also add that this: Browser.Engine.trident is the problematic check, and from the gyst of the code looks like it might occur else where too...
aha! another update:
i found this while looking through [an old] support thread:
you'll need to update to 1.2.5 or 1.3. Previous MooTools versions are not supported by IE9.
so an update to the script should fix your problem, hopefully it won't introduce more bugs... you can get it here: http://mootools.net/download, you might want to try that 1.2.5 version at the top of the page since it will have the least amount of changes...
good luck -ck

Javascript Variable Sometimes Undefined

I know this question has been asked several times, but I couldn't seem to find a solution that worked for me in any of the previous questions. I have a variable that gets set when my HTML page is done loading, but sometimes when my code tries to access that variable, it says that it is undefined. I'm not sure why, since I believe I am waiting for everything to load properly. This exception seems to happen randomly, as most of the time all the code runs fine. Here's a simplified version of my code:
var globalVar;
function initStuff(filePath) {
// I wait till the HTML page is fully loaded before doing anything
$(document).ready(function(){
var video = document.getElementById("videoElementID");
// My parseFile() function seems to run smoothly
var arrayOfStuff = parseFile(filePath);
if (arrayOfStuff == null) {
console.error("Unable to properly parse the file.");
} else {
setGlobalVariable(arrayOfStuff);
video.addEventListener("play", updateVideoFrame, false);
}
});
}
function setGlobalVariable(arrayOfStuff) {
window.globalVar = arrayOfStuff;
}
function updateVideoFrame() {
// A bunch of other code happens first
// This is the line that fails occasionally, saying
// "window.globalVar[0].aProperty.anArray[0] is undefined"
var test = window.globalVar[0].aProperty.anArray[0].aProperty;
}
The only thing that I can think of that might be causing this problem is some sort of synchronicity issue. I don't see why that would be the case, though. Help please!
Edit:
In case the asynchronicity issue is coming from my parseFile(xmlFile) method, here is what I'm doing there. I thought it couldn't possibly be causing the issue, since I force the method to happen synchronously, but in case I'm wrong, here it is:
function parseKML(xmlFile) {
var arrayOfStuff = new Array();
// Turn the AJAX asynchronicity off for the following GET command
$.ajaxSetup( { async : false } );
// Open the XML file
$.get(xmlFile, {}, function(xml) {
var doc = $("Document", xml);
// Code for parsing the XML file is here
// arrayOfStuff() gets populated here
});
// Once I'm done processing the XML file, I turn asynchronicity back on, since that is AJAX's default state
$.ajaxSetup( { async : true } );
return arrayOfStuff;
}
The first thing you should do in your code is figure out which part of:
window.globalVar[0].aProperty.anArray[0]
is undefined.
Since you have multiple chained property references and array references, it could be many different places in the chain. I'd suggest either set a breakpoint right before your reference it examine what's in it or use several console.log() statement sto output each nested piece of the structure in order to find out where your problem is.
console.log("globalVar = " + globalVar);
console.log("globalVar[0] = " + globalVar[0]);
console.log("globalVar[0].aProperty = " + globalVar[0].aProperty);
console.log("globalVar[0].aProperty.anArray = " + globalVar[0].aProperty.anArray);
console.log("globalVar[0].aProperty.anArray[0] = " + globalVar[0].aProperty.anArray[0]);
If the problem is that globalVar isn't yet set, then you have a timing problem or an initialization problem.
If the problem is that one of the other properties isn't set, then you aren't initializing globalVar with what you think you are.
You may also want to write your code more defensibly so it fails gracefully if some of your data isn't set properly.
You need to use defensive programming.
http://www.javascriptref.com/pdf/ch23_ed2.pdf
Example:
var video = document.getElementById("videoElementID") || 0;
-
if( video && video.addEventListener ){
video.addEventListener("play", updateVideoFrame, false);
}
Here's another version of your code.
window.globalVar = globalVar || [];
function setGlobalVariable(arrayOfStuff) {
window.globalVar = arrayOfStuff;
}
function updateVideoFrame() {
// A bunch of other code happens first
// This is the line that fails occasionally, saying
// "window.globalVar[0].aProperty.anArray[0] is undefined"
if( window.globalVar ){
var g = window.globalVar || [];
var d = (g[0] || {})["aProperty"];
// etc...
}else{
console.error( "test error." );
}
}
function initStuff(filePath) {
// I wait till the HTML page is fully loaded before doing anything
$(document).ready(function () {
var video = $("#videoElementID");
// My parseFile() function seems to run smoothly
var arrayOfStuff = parseFile(filePath) || [];
if (arrayOfStuff == null || video == null ) {
console.error("Unable to properly parse the file.");
} else {
setGlobalVariable(arrayOfStuff);
video.bind("play", updateVideoFrame);
}
});
}

How to ensure my javascript function only executes after all external javascripts are loaded?

I am developing a WebPart in SharePoint,and I need to draw something in my WebPart using excanvas.js.But sometimes it shows nothing.The error message is:
Object doesn't support property or method 'getContext'
When I debug it,it breaks at here:
var ctxBg = document.getElementById(backgroundId).getContext("2d");
The "backgroundId" is the id of one canvas element.
This error happens not every times,just sometimes,so I think if my js function is executed before the excanvas.js is loaded.I register the excanvas.js with the code:
this.Page.ClientScript.RegisterClientScriptInclude("ExCanvasJs", "wpresources/MyWebPart/js/excanvas.js");
So how to ensure my function is executed after the excanvas.js is loaded?Or I'm wrong at this problem?Would you give me your advice?
my js function:
function DrawMeter(meter, contextCollection) {
var backgroundId = meter.meterbackground;
var pointerId = meter.meterpointer;
var containerId = meter.metercontainer;
if (contextCollection != null && contextCollection.length > 0) {
for (var i = 0; i < contextCollection.length; i++) {
DrawSingleMeter(backgroundId + "_" + i, pointerId + "_" + i, containerId + "_" + i, contextCollection[i]);
}
}
function DrawSingleMeter(backgroundId, pointerId, containerId, context) {
var ctxBg = document.getElementById(backgroundId).getContext("2d");
var ctxPointer = document.getElementById(pointerId).getContext("2d");
drawing...
}
you need to put it into a page ready handler. otherwise at the time the javascript is executed the element might not be available yet.
consider using jquery or another js library for this, if you can't use it here's a link on how to use the native js version of the onload event

getElementById().appendChild() refuses to work!

Im trying to append some JSON data from the last.fm API,
I have been using alert() at several stages to verify the API is being parsed correctly and it is,
This has led me to the conclusion that getElementById().appendChild() doesn't work, below is the URL to the test page I have set up:
http://mutant-tractor.com/tabtest.html
Code here
function calculateDateAgo(secAgo) {
var agoString, agoRange, agoScaled;
if(secAgo >= (agoRange = 60*60*24))
agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"days":"day") + " ago"
else if(secAgo >= (agoRange = 60*60))
agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"hours":"hour") + " ago"
else if(secAgo >= (agoRange = 60))
agoString = (agoScaled = Math.floor(secAgo/agoRange))+" "+(agoScaled>1?"minutes":"minute") + " ago"
else if(secAgo >= -60)
agoString = "blastin' out now";
else
agoString = "soon ;)";
return agoString
}
function truncateName(name, l) {
return name.length > l ? name.substr(0,l-2) + "\u2026" : name
}
function lfmRecentTracks(JSONdata) {
try {
var eImg, eLink, eSpan, divTag, eWrapper;
var oTracks = new Array().concat(JSONdata.recenttracks.track);
for (var i = 0; i [lessthanhere] oTracks.length; i++) {
//insert track link
spanTag = document.createElement("span");
spanTag.className = "lfmTrackInfoCell tabslider";
eLink = document.createElement("a");
eLink.appendChild(document.createTextNode( truncateName(oTracks[i].name, 25) ));
//alert(truncateName(oTracks[i].name, 25));
spanTag.appendChild(eLink);
eLink.href = oTracks[i].url;
//alert(oTracks[i].url);
eLink.target = "new";
eLink.className = "lfmTrackTitle";
document.body.appendChild(spanTag);
//insert artist name
eSpan = document.createElement("span");
eSpan.appendChild(document.createTextNode(truncateName(oTracks[i].artist["#text"], 22) ));
//alert(truncateName(oTracks[i].artist["#text"], 22));
eSpan.className = "lfmTrackArtist";
document.body.appendChild(eSpan);
//insert date
eSpan = document.createElement("span");
spanTag.appendChild(eSpan);
eSpan.appendChild(document.createTextNode( (typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts)) ));
//alert((typeof oTracks[i].date=="undefined"?"now playing":calculateDateAgo(new Date().getTime()/1000 - oTracks[i].date.uts)));
eSpan.className = "lfmTrackDate";
document.body.appendChild(eSpan);
}
} catch(e) {}
}
The only way it works is by using document.body.appendChild()
I'm calling the script in the head if that makes a difference?
The div I'm trying to attach them to are 4 different divs i.e. in the for loop each loop needs to reference a different element,
Thanks in advance!
Myles
I'm calling the script in the head if that makes a difference?
You won't be able to getElementById() if the document body hasn't even been parsed. In other words, you need to run your code in an window.onload function, or place it at the very bottom of your body.
Also, remove the try/catch while testing, it will only hide errors.
Are you sure that the element you're trying to get has been loaded into the DOM? You said that your script runs in the head tag (which loads before the rest of the body loads). It's possible that your script is being run before the DOM element you're searching for exists, and therefore it can't find it and therefore it can't add to it.
There's no guarantee that the HTML will have finished parsing by the time the JavaScript executes. There are several ways of doing what you want, with different performance characteristics.
You can put your code in a function and assign it as the load event handler for the window object. This has the downside of waiting until all resources for the page have finished loading, not just the HTML. This often slows down page load times, as you need to wait for things like slow ad servers etc.
You can put your code in a function and call it from bottom of the document.
You can use a JavaScript library such as jQuery to execute your JavaScript when the DOM has finished loading. There isn't a simple way of doing this that works cross-browser, so it's simplest not to reinvent the wheel and just to use what is already a mature solution.

Categories

Resources