I'm trying to write a function that will dump a recursive tree of window for all browsers. A problem that I immediately realized I was going to have, had to do with infinite objects (window.window.window.window). Just for laughs, I tried it anyways, and I got an error as I expected. Uncaught RangeError: Maximum call stack size exceeded (testing in Chrome)
So the first approach to check against objects that were going to cause this was simply:
if (variable != 'window' && variable != 'top' && variable != 'self' && variable != 'frames')
I'm thinking maybe that would have worked, and I simply missed a couple. It was a good theory, but I still get the maximum stack error. So I decided to type window in Chrome's console, and manually look for all of the [DOMWindow] types, to add to this list. While doing that, I noticed the Infinity: Infinity value, which brought me to my next approach:
if (typeof namespace[variable]['Infinity'] === 'undefined')
I still got the maximum stack error with that, so I did a bit of Google searching, and learned about isFinite, so now I have: (edit: actually I just realized isFinite isn't what I thought it was)
if (isFinite(tree[variable]))
The error finally went away, but the problem with this approach is that all objects in window are returning false for this, so the recursion fails. I realize that some of the approaches probably aren't even cross-browser compatible, but it would be nice if I could get it to at least work in one browser in the mean time.
So how can I check for objects that are going to cause an infinite loop?
Here's my code, just for anyone who might be interested:
(function () {
window.onload = function () {
window.onload = ''; // don't want to get our own code
console.log((function (namespace) {
tree = {};
for (var variable in namespace) {
/* gonna need these later
var variable_typeof = typeof namespace[variable],
variable_object_tostring = Object.prototype.toString(namespace[variable]);
*/
//if (variable != 'window' && variable != 'top' && variable != 'self' && variable != 'frames')
//if (typeof namespace[variable]['Infinity'] === 'undefined')
if (isFinite(tree[variable]))
tree[variable] = arguments.callee(namespace[variable]);
else tree[variable] = 'Infinity';
}
return tree;
})(window)); // Start from root
}
})();
Update:
Here is a working product of what I finally came up with, for anyone interested.
GGG is worthy of mention for his help.
function loop (namespace) {
if (namespace['__infinite_test']) return '[[recursion]]'; // It's infinite
namespace['__infinite_test'] = true; // Note that we've been through this object
var tree = {};
for (var variable in namespace) {
try { // For an issue in Chrome throwing an error
namespace[variable]['__tester'] = null;
delete namespace[variable]['__tester'];
}
catch (e) {
tree[variable] = namespace[variable];
continue;
}
if (namespace.propertyIsEnumerable(variable)) tree[variable] = loop(namespace[variable]);
else tree[variable] = namespace[variable];
}
return tree;
}
console.log(loop(window));
One way to prevent infinite recursion in your problem is to keep track of a list of all objects you have already visited and if you encounter an object you've already visited, you don't recurse into it.
When you encounter an object that is not in your list, you add it to your list and then recurse into it.
Related
I need to run through a stack of HTML Elements. But all my attempts to write a recursive function didn't work.
In the snippet below I cannot return value from if statement and afterwards from a function itself.
I can console.log the information I need, it gets there, but trying to return it, it doesn't work.
I never had such an issue with returning some data that's why I decided to display it here so as to let a fresh-eye to revise the code.
function findElementByDataValue(target: EventTarget, data: {key: string, value: string}){
if (target.dataset[data.key] === data.value) {
return target;
};
if (target.children.length > 0) {
for (const child in target.children) {
const element = target.children[child];
// I tried to return "recursive" function here too. "Return" Abrupt execution (as it should)
if (element.children && typeof element === 'object') {
findElementByDataValue(element, data);
}
}
}
}
if you have any ideas or noticed an issue with my recursive function, I would appreciate any help.
Using the return keyword like you are doing.
Your second function calls the first function without returning the value returned by the first function:
Replace
if (element.children && typeof element === 'object') {
findElementByDataValue(element, data);
}
with:
if (element.children && typeof element === 'object') {
return findElementByDataValue(element, data);
}
In general, run your code in a debugger (popular web browsers provide debuggers) to see what is going on.
See some debuggers documentation:
https://developer.mozilla.org/en-US/docs/Tools/Debugger
https://developers.google.com/web/tools/chrome-devtools/javascript/
If you are new to JavaScript, I suggest looking into unit testing and test-driven development.
Writing tests (early) will help you think of what can go wrong with your code and write more robust functions. Jasmine is nice, this article suggests many other JavaScript unit testing options
I'm trying to using a make a lock variable in Node-Red. for that I've wrote the following function!
global.set('lockvariable',0);
var payload = msg.payload;
if (msg.payload[0].value === 0 && global.get('lockvariable')=== 0){
msg.payload =global.get('lockvariable');
global.set('lockvariable',1);
} else if (global.get('lockvariable') === 1){
msg.payload = global.get('lockvariable');
}
return msg;
The Problem that I have is that the return value is always 0. I guess because I'm writing the initialization the first line ! since I'm from the C world I don't get what I'm missing here!
Thanks for any hint!
If you want to initialise a global variable you probably want to do it on a separate flow with and inject node set to fire on Node-RED startup to trigger it.
The other option is to gate the setting at the start of the function with a test to see if the context variable has already been set.
if (global.get("lockvariable") == undefined) {
global.set('lockvariable',0);
}
...
I don't know if the issue lies with me or with OS X.
I have this AppleScript:
tell application "Caffeine"
if active then
turn off
else
turn on
end if
end tell
I translated this to this JavaScript
caffeine = Application("Caffeine");
if (caffeine.active)
{
caffeine.turnOff();
}
else
{
caffeine.turnOn();
}
However caffeine.turnOn(); is never executed, no matter how often I run it. If Caffeine is active, it is turned off, otherwise nothing. The AppleScript equivalent runs. caffeine.turnOn(); and caffeine.turnOff(); by itself also run fine. I can't imagine, that JavaScript for OSA is really this broken, that even this doesn't work.
caffeine.active might be a function, which when not called will always be truly:
var my_fn = function() {};
if (my_fn) console.log('my_fn is truly');
Call the function:
var caffeine = Application("Caffeine");
if (caffeine.active()) {
caffeine.turnOff();
}
else {
caffeine.turnOn();
}
A way to check it, is to simply log the value:
console.log(caffeine.active); // function() { .... }
// or using typeof
console.log(typeof caffeine.active); // "function"
Why would the difference between these two lines of code create a bug that cause jquery to loop endlessly in iOS(Safari and Chrome)? The loop did not occur in any other browser.
if ($('[name="loadingTime"]') != undefined) {...
vs
if ($('.loadingTime') != undefined) {...
When we targeted by class and not name attribute the loop bug went away. Any ideas or explanations?
Upon further investigation the bug was discovered in another part of the code. Here's what happened:
loadInterval: function() {
var settimer = $('[name="loadingTime]').val();
var interval = setInterval(function() {
if (settimer == 0) {
window.clearInterval(interval);
$('[id^="interstitial-container"]').remove();
};
settimer--;
if (settimer >= 0) {
$('.ncount').text(settimer);
}
}, 1000);
}
in
var settimer = $('[name="loadingTime]').val();
we missed a closing quote after loadingTime! which the js returned as undefined and iOS didn't handle it gracefully so var settimer wasn't set to zero so whenever that function loadInterval was called it was undefined and we checked whether we needed to load based on undefined or not. in our case it wasn't and continued to load over and over always getting an undefined response but without an error. I think...
I made this javascript method that I altered from an existing script that I found online that should rotate showing an indefinite number of '.testimonial' divs. The script works fine in chrome and firefox, but doesn't compile in internet explorer, unless you use f12 to start the debugging of the script. Is there a better way to write this script? I have looked online for ideas but haven't been able to find anything. I imagine that the issue is with the console.log(testimonialCount); statement, but am unsure of a better way to write it. Any help would be greatly appreciated. Thanks.
//rotate testimonials script
jQuery('.testimonial').hide();
var testimonialCount = $('.testimonial').length;
console.log(testimonialCount );
var currentItem = 0;
var timeout;
timeout = window.setTimeout((function(){switchDiv();}));
switchDiv = function() {
if (currentItem == testimonialCount - 1) {
jQuery('.testimonial').eq(testimonialCount - 1).hide();
currentItem = 0;
jQuery('.testimonial').eq(0).fadeIn();
timeout = window.setTimeout((function(){switchDiv();}),7000);
}
else {
jQuery('.testimonial').eq(currentItem).hide();
currentItem = currentItem + 1;
jQuery('.testimonial').eq(currentItem).fadeIn();
timeout = window.setTimeout((function(){switchDiv();}),7000);
}
}
When the IE developer tools are not open, there is no console object.
Therefore, calling console.log() throws an error.
To prevent that, you can check if ('console' in window) and make your own dummy console (or just don't log anything) if it isn't.
A few notes:
Add this to the very top of your script:
window.console = console || { 'log' : function(){} };
This defines window.console in case it isn't. Doesn't do anything except avoid errors.
Next take out the parenthesis around your timeout functions:
timeout = window.setTimeout(function(){ switchDiv(); },7000);
... or just simplify further:
timeout = window.setTimeout(switchDiv,7000);
I am being picky here but:
currentItem = currentItem + 1; is the same as currentItem++;
Another picky thing window.setTimeout is the same as setTimeout.