I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.
Related
This may be a quite naive question but I really need some help.
Prior to writing this post, I was programming on JSBin. Turns out without me realizing, I ran a setInterval loop prompting for userInput and it kept on looping, making me unable to click anywhere to change the code to fix the loop. It kept on repeating and repeating. It got to the point where I had to refresh and lose all my hard-written-code (I was not logged in, so my code was not saved)! I want to avoid that next time.
So, my question is how do I stop any such kind of setInterval Loops, so that I am able to access my code and change it and re-run it. Below is a code that demonstrates my issue, if you try running it on JSBin.com (obviously, it is not the code I wrote before). As you can see, I can not click on my code to change it (or save it) in any way, which means I lose all my code!
This may seem like a useless question, but I really want to know ways to fix it and perhaps fixing it from the developer tools will help me be familiar with the overwhelming set of tools it has :P. So please help me if you know a solution.
Thank you for taking your time to help me! I appreciate it.
setInterval(demo,1);
function demo()
{
var name = prompt("Enter your name: ");
}
Another option is to search the developer tools "Elements" panel for the iframe (this should be doable even if the main document is unresponsive due to prompt's blocking) - then, just right click the iframe element and remove it, no need to type any Javascript. (or, if you want you can select the iframe with querySelector and remove it, eg document.querySelector('iframe').remove())
That's kind of a hack and should only be used in cases like the one exposed in OP but,
About all implementations use integers as timerid that just get incremented at every call.
So what you can do, is to clear all timeouts that were created on the page.
To do so you need to first get to which timerid we are, then call cleatTimeout or clearInterval (they do the same) in a loop until you reach the last call:
function stopAllTimers() {
const timerid = setTimeout(_=>{}); // first grab the current id
let i=0;
while(i < timerid) {
clearTimeout(i); // clear all
i++;
}
};
btn.onclick = stopAllTimers;
// some stoopid orphan intervals
setInterval(()=>console.log('5000'), 5000);
setInterval(()=>console.log('1000'), 1000);
setInterval(()=>console.log('3000'), 3000);
const recursive = () => {
console.log('recursive timeout');
setTimeout(recursive, 5000);
};
recursive();
<button id="btn">stop all timeouts</button>
Assuming the dev tools are closed, hit esc and f12 nearly simultaneously. This should open the dev tools. If it doesn't keep trying until it does.
Once they are open, hit esc and f8. Again, retry til it halts javascript execution at some arbitrary point in the code.
In the "sources" tab locate the generated script for what you wrote (offhand I don't know how it would look like from within JSBin) and literally delete the var name = prompt("Enter your name: "); line. Hitting f8 again will continue execution as if the "new" code is running. This should free you up to copy/paste your code from the site itself before you refresh the page
Does anyone know of a way to eval a string so that if it (or a function it defines) generates an error, the line and column numbers shown in the stack trace will be offset by an amount specified in advance?
Alternatively, suppose I want to break up a long source string into chunks and evaluate them separately, but still get stack traces that look as though the entire string was evaluated in one go. Is there any way to achieve this effect, except for using empty lines and columns? (I need a browser-based solution, preferably cross-browser, but I can settle for something that works on at least one of the major browsers.)
I don't think is it possible because the underlying mechanism that is assumed working is actually deprecated. For security reasons browsers don't pass the error object to Javascript anymore.
However, since you are working with a custom programming language that gets compiled into Javascript, you know what the structure of the resulting script will be. You could also introduce statement counters in the resulting Javascript, so you can always know what the last thing executed was. Something like:
function(1); function(2);
function(3);
could be translated as:
var __column=0;
var __line=0;
function(1); __column+=12;
function(2); /*__column+=12;*/ __line++; __column=0;
function(3); /*__column+=12;*/ __line++; __column=0;
Where 12 is "function(n);".length.Of course, the resulting code is ugly, but you could enable this behaviour with a debug flag or something.
The best solution I've found so far is to prepend a sourceURL directive to each string before it's eval'ed, giving it a marker in the form of a unique file name in the stack trace. Stack traces are then parsed (using the parser component stacktracejs) and corrected by looking up the line offsets associated with the markers.
var evalCounter = 0;
var lineCounter = 0;
var lineOffsetTable = {};
function myEval(code) {
lineOffsetTable[evalCounter] = lineCounter;
lineCounter += countLines(code);
return eval("//# sourceURL=" + (evalCounter++) + "\n" + code);
}
window.onerror = function(errorMsg, url, lineNumber, column, e) {
var stackFrames = ErrorStackParser.parse(e);
logStackTrace(stackFrames.map(function(f) {
if(f.fileName in lineOffsetTable)
f.lineNumber += lineOffsetTable[f.fileName];
return f;
}));
};
Unfortunately, this only works in Firefox at the moment. Chrome refuses to pass the error object to the onerror callback (a problem which only happens with eval'ed code, strangely enough) and IE ignores the sourceURL directive.
we are facing a pretty strange problem in our web aplication on IE11 (other IE versions work fine).
The application is based on SmartGWT ( http://www.smartclient.com/product/smartgwt.jsp ) - GWT wrapper on SmartClient javascript framework.
IE11 goes into never ending cycles.
It happens very randomly and we have no steps to reproduce it. The complexity of our application makes it impossible to post a sample of the code.
Most often it happens when users work with the application, then they minimize the browser window and after some time they restore it and try to continue working.
The never ending cycles are caused by strange comparison results, when expression 'true === true' results in false.
The comparison code is:
$wnd.isc.isA.Canvas(obj) === true
The code is executed in scope of iframe containing javascript compiled by GWT.
$wnd is the main(top) window of the web application where the SmartClient javascripts are loaded.
The isc.isACanvas(..) is a method returning true or false, depending on whether the object passed as parameter is of Canvas type - Canvas is a special class from SmartClient framework not the HTML Canvas element.
isc.isA.Canvas = function (object) {
return (object != null && object._isA_Canvas);
}
_isA_Canvas is set on object to true (boolean true not 'true' as a string) when the object is being created.
I have added some testing code to the part where the problematic comparison is used - here's a simplified version:
var trueCheckCount = 0;
function isCreated(id){
var obj = $wnd.window[id]; // objects are stored in window by id
var comparisonResult = $wnd.isc.isA.Canvas(obj) === true;
if (!comparisonResult ) {
if ($wnd.isc.isA.Canvas(obj) == true) {
alert("TRUE != TRUE (was OK: " + trueCheckCount + "x before)");
} else {
trueCheckCount++;
}
}
return comparisonResult;
}
In different test runs, the alert was shown after different number of passes. E.g. on the first run it passed 358 698 times, on the second run it passed 330 125 times …
Does anybody have any idea what could be the problem?
How can 'true !== true' ever happen?
Environment:
IE11 (Windows7/8)
SmartGWT: v9.0p_2014-03-02/LGPL Development Only (built 2014-03-02)
GWT: 2.4
Some additional debugging information can be seen on screenshot:
https://drive.google.com/file/d/0B8h18b-AMFzXa3NZZ2dOX2txb1k/edit?usp=sharing
The problem was caused by a bug in IE11.
MS released the fix. It's included in December Internet Explorer Cumulative Update KB3008923.
Installing this update solved our problem.
The technical details we got were:
The bug in question is related to reclaiming of JIT functions and cross-site thunks.
A function with a cross-site thunk is getting reclaimed.
We then change the entryPoint to the InterpreterThunk, losing the cross-site thunk in the process.
Marshalling isn't done when calling this function.
In the repro in question, we end up with a Boolean True object from a different scriptContext, which doesn't match the one in the current scriptContext when comparing with ===.
The problem is 99% in SmartGWT. I recommend you to report this issue to the SmartGWT team and use the latest GWT compiler of course. We dealt with SmartGWT before and from my experience I should say it was a pretty buggy library back then.
You have two issues in your debug code that could be giving you a false impression of the issue:
if ($wnd.isc.isA.Canvas(obj) == true) {
alert("TRUE != TRUE (was OK: " + trueCheckCount + "x before)");
} else {
Firstly, you're doing a double-equal comparison here, where earlier you'd done a === comparison. These are different, and will give different results if the variable being tested is not a boolean.
So for example, if $wnd.isc.isA.Canvas(obj) is outputting an integer 1 or something like that rather than true, then you would get exactly the problem you're describing.
Rather than doing another comparison why not get an accurate picture of the contents of the variable by using JSON.stringify
console.log(JSON.stringify($wnd.isc.isA.Canvas(obj)));
This will show you exactly what the value is, which should then show you fairly clearly why the comparisons aren't doing what you expect.
Secondly, using an alert() box to show errors is not a good idea, especially in a complex application. You should always use console.log() for debugging rather than alert(), because alert() can cause some JS code to alter its behaviour, which can make debugging really difficult.
This is because alert() blocks JS execution while it's displaying, which means that any event handlers or other asyncronous code that get triggered during that time can end up running out of the expected sequence.
I don't think this is necessarily the issue for you here, given the code sample provided, but if you have any Ajax calls, setInterval()s or other similar async code anywhere then you really do need to avoid using alert() for debugging purposes.
Hope that helps.
I'm working on localhost (so would expect to not have any domain-related probs as here).
On a page I'm using a bit of JS to modify the content of a span in the opening-window. It does not work.
When checking my code to find the control, it works (using FF dev-tools calling my Increment-function or checking the console.log-output): $('#uploads_Count')returns an object of type HTMLSpanElement. However, trying to access the same control from an opened window's console with window.opener.$('#uploads_Count'), this returns an HTML-Document, seemingly the entire page. Why is this not working, what am I missing here?
Here is function that is supposed to increment the counter contained in the span whose id is given as argument:
function Increment(ctrl)
{
var gef = $("#" + ctrl);
if (!gef) // did not find control, maybe on opener?
{
gef = window.opener.$("#" + ctrl);
}
console.log(gef);
cnt = parseInt(gef.text() , 10);
cnt++;
gef.text(cnt);
}
The HTML is trivial:
<span id="uploads_Count">0</span>
If $(selector) returns an element (such as HTMLSpanElement), rather than a collection of elements (would look like [<span id="uploads_Count"></span>] in most dev tools), then you're not calling jQuery.
Dev tools in A-grade browsers tend to introduce $ as a selector function. It is available in the developer console only.
If window.jQuery exists, then it's likely that jQuery.noConflict() was called, in which case you should use window.opener.jQuery.
Found it!
The way I checked if the control was found, was wrong. Instead of if (!gef)I should have used if (!gef.length). Found the explanation here.
I see a very funny behaviour in my page when it comes to IE6 and IE5.5. I have a script (supersleight if you know about it) that puts PNG's back in business when dealing with IE6 and IE5.5. During execution of this, I want to change the background into using the Explorer alpha filter (if Javascript is turned on, use filter, otherwise stick to solid white).
I do this by:
if(document.getElementById('transparency') != null)
document.getElementById('transparency').style.filter= "alpha(opacity=60)";
...transparency is the id of the object in question.
Putting this at the end of the HTML page (or anywhere after 'transparency' was initiated) results in the script working. Putting it at the very end of the exterior script (deferred) however results in the filter NOT being applied.
However, when I remove the if statement and just tell the browser to use the filter it works (however only a few of the pages has got the 'transparency' id).
I tried to apply the if statement differently by using an alert box and trying both != null and == null and I get nothing.
This made me very curious so I tested this:
var tt = 5;
if(tt == 5)document.getElementById('transparency').style.filter= "alpha(opacity=60)";
Which gave an even stranger result with an error screen saying
tt is undefined
All of this runs perfectly in IE 7 and above...
I realize this is really two different issues but still...
Can anyone give me a clue as to what's going on?
Does this work?
var t = document.getElementById('transparency');
if (t && t.style) t.style.filter="alpha(opacity=60)";
How about this?
try {
document.getElementById('transparency').style.filter= "alpha(opacity=60)";
} catch (e) { }