Recursively find variable in page's javascript from firefox extension - javascript

So I'm working on a firefox extension that we'll be using internal (thus not terrible worried about security), and I'd like to be able to interact with the functions and values defined in the javascript of a webpage. I'm having trouble reliably finding variable definitions.
Take for example gmail, it has a VIEW_DATA list that has email subjects, etc to display. It can be accessed through window.content.frames.wrappedJSObject.VIEW_DATA, but this doesn't seem to always work for me.
Is there a reasonable way to reliably search (possible recursively) the javascript of a page for a given variable from a firefox extension?

Is this what you are looking for?
var inspected = [];//prevent infinite loop; top===window
function inspector(obj) {
inspected.push(obj);
for(var prop in obj) {
console.log(prop);
if(!is_duplicate(obj) && typeof obj[prop] == 'object')
inspector(obj[prop]);
}
}
function is_duplicate(obj) {
for(var i = 0; i < inspected.length; i++) {
if(inspected[i] === obj)
return true;
}
return false;
}
These functions will run through all of the properties of an object, walking all the way down the tree of objects. I just log the properties, but you may wish to do something more useful. Try inspector(window) and watch your firebug console get filled.

Related

Concise way to null check a deeply nested object?

JavaScript for browser
I need to test that one deeply embedded property is not null and if this condition is true, then to change its property. But any its parent can be null also. Therefore I am to check each item in the chain... Therefore I write such ugly code:
if(window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet){
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item;
}
Is there a shorter method of checking?
(I was the original poster proposing the try-catch method, but based on the discussion on that post you were worried about performance. Here's an alternate approach.)
You can use prototype methods to implement a safe method of accessing subproperties. Here is a method which can safely test for the existence of a nested property:
// Indicates whether an object has the indicated nested subproperty, which may be specified with chained dot notation
// or as separate string arguments.
Object.prototype.hasSubproperty = function() {
if (arguments.length == 0 || typeof(arguments[0]) != 'string') return false;
var properties = arguments[0].indexOf('.') > -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x < properties.length; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return false;
}
return true;
};
A full set of methods can be found here, with sample code.
Timings can be run here, and indicate that using the try-catch method may run a couple of orders of magnitude slower than your original approach when errors are thrown, but is otherwise quite fast. The prototype methods are more generic and can lead to a more declarative style, while offering much better performance than try-catch misses, but obviously not quite as good as hand-crafting if statements each time and/or try-catch without a miss.
I've also blogged about this approach.
Syntax wise I don't think so, but I recommend refactoring at least.
var getCurrentStageSet = function(window){
return window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet
}
var setSelectedEntity = function(currentStageSet, item){
currentStageSet.selectedEntity = item;
}
By abstracting this logic your actual set of the property will be more readable, and reusable:
var currentStageSet = getCurrentStageSet(window);
if (currentStageSet){
setSelectedEntity(currentStageSet, item);
}
For such a trivial, self-contained piece of code, it's probably not unreasonable to just catch and ignore the error (possibly log) e.g.
try {
if (window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet) {
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet = item;
}
} catch (e) {
// log the error but continue
}
Not sure what else could really go wrong in this type of check, alternatively you could catch a TypeError specifically but not sure it would really matter all that much.
I generally wouldn't recommend catch all's but in this case it seems self contained enough to not be a huge risk.
Anything beyond that requires effort e.g. building an object decorator or a fluent interface type solution, seems overkill to me though.
You can create some variables to get code more readable
var tech = window[rootNamespace].vm.tech;
var workType, curVariant, curVer, curWorkSection;
if(tech){
workType = tech.currentWorkType
}
if(workType){
curVariant = workType.currentVariant;
}
if(curVariant){
curVer =curVariant.currentVersion;
}
if(curVer){
curWorkSection = curVer.currentWorkSection;
}
if(curWorkSection && curWorkSection.currentStageSet){
curWorkSection.currentStageSet.selectedEntity = item;
}
This is the most compact syntax possible in basic JavaScript. It avoids all the null-checking by using error-trapping instead. None of the other answers are as compact because the language is simply missing the feature you're after from C#.
Apparently, I'm being down-voted by the authors of the other, much less compact answers, but this is nevertheless the only single-line answer. Note that other approaches listed here have you creating multiple functions, even. :| If you want compact, this is it.
try { window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item; } catch (err) {}

Screeps: write debug output to console?

Is there a way of getting screeps code to print strings to the console (or anywhere really) for simple debugging purposes?
You can use standard console.log method for that.
I use the following to print objects to the console:
console.log(JSON.stringify(<myVariable>))
I can't find how to do this in Docs. Had to write something like this:
module.exports = function () {
var log = Memory.log;
if(log === null || log === undefined){
log = Memory.log = [];
}
var parts = ["["+Game.time+"]"];
for(var i in arguments){
parts.push(arguments[i]);
}
var msg = parts.join(" ");
log.push(msg);
if(log.length > 10){
log.shift();
}
}
Will be grateful if someone can provide a better solution.
Sometimes when you do console.log you get unhelpful string representations of objects, something like like "[Object]".
If you want to drill down into the object and check out it's properties, the easiest solution is to open your browser's console. The devs made it so any console.log in your script will also reach the standard browser console. I believe it works in all major browsers.

How to copy an array of JSON objects in javascript

I have an array of objects in JavaScript. e.g. current_films[0].f_name, current_films[0].f_pattern etc. I want to copy the array into another similiar to the following:
for(var i=0; i<current_films.length; i++)
{
if(current_films[i].f_material == Value)
{
temp[i] = current_films[i];
}
}
However, there seems to be an inexplicable problem when I do this. By inexplicable problem, I mean that the code does not execute and the array is not copied as I desire.
Any help would be greatly appreciated.
Thank you!
P.S. Could you please mention why the above code would not work? As in, if I put an alert("Reached here");, it's not getting executed. Any ideas why its so?
One problem I see is that you set temp[i] to the value which means there would be gaps in the temp array. You could use push() to append the value to temp so you don't need to manage two sets of indices.
You could also use JavaScript's Array.filter() to do this a little easier. Filter will return a new array of the values from the original array where your function returns true.
var temp = current_films.filter(function(film) {
return (film.f_material === Value);
});
P.S. Could you please mention why the above code would not work? As in, if I put an alert("Reached here");, it's not getting executed. Any ideas why its so?
I'd guess f_material is not defined for every element in the array.
If that's the case I'd use
if(typeof(current_films[i].f_material)!=='undefined')
{
if(current_films[i].f_material == Value)
{
temp[i] = current_films[i];
}
}
Anyway I'd suggest you to get familiar with the browser's javascript debugger (assumed that code runs in a browser)
Finally note that you're not copying the array/object:
temp[i] is a reference to current_films[i]
Modifying current_films later in the code will result in modifying temp
If that's not the behaviour desired Google for "javascript object copy".

Loop through JavaScript object - which one is faster?

between saving a key name like this
for(var key in this.data)
{
var key_name = key;
for(key in this.data[key_name].content){
alert(this.data[key_name].content[key].score);
}
}
or making checkpoint objects for every parent node
for(var key in this.data)
{
var obj = this.data[key];
for(key in obj.content){
var inner_obj = obj.content;
alert(inner_obj[key].score);
}
}
which one has better performance? any other suggestion?
Only one way to know for sure: measure it.
http://jsperf.com/so-question-9853395
(click through for up-to-date results)
You should use a combination of the two:
for(var key in this.data)
{
var inner_obj = this.data[key].content;
for(ikey in inner_obj){
alert(inner_obj[ikey].score);
}
}
This would be the fastest way to do it compared to your two proposed solutions.
Please note that I renamed key to ikey inside the inner loop to avoid confusion.
After verification, my solution is indeed the fastest:
(source: minus.com)
Theoretically at least, the fastest would be to get the object reference outside the inner loop. In practice the difference might not be that big, if the browser is already doing this internally. Also, the performance might differ between browsers.
for(var key in this.data) {
var obj = this.data[key].content;
for(var key2 in obj){
alert(obj[key2].score);
}
}
Note that you should use separate variables for the loops, otherwise it will be hard to follow which values is in the variable at which point when you put some actual code in the loops.
Edit:
When I measure the performance in IE and Firefox, I find that this method is slightly faster than the methods proposed in the question. However, the difference is so small that you should not be concerned with it. Either method works just fine, and any performance concerns would be what you actually do with the values inside the inner loop.
From an code interpreter's point of view, the first one should be faster. In the second example every loop you have to access the this.data array.
In modern browsers you might have a good Just In Time compiler. If this is true, this JIT could possibly 'translate' your code in the exact same opcode.
So you have to test in different browsers by executing your code several times (I mean several thousand times) and measure the execution time.
If you don't have a JIT compiler in your browser folloding would be even faster.
var data = this.data
for(var keyA in data)
{
var content = data[keyA].content;
for(keyB in content){
alert(content[keyB].score);
}
}

JQuery "Constrain" plugin - Weird Javascript Error

Background: Our web app uses the jquery.constrain.js plugin to handle data entry in some text boxes, to only allow valid characters to be added. This plugin allows various constraints upon the data, using regular expressions, whitelist/blacklist characters, etc. Until today, we were running the 1.0 release of this plugin, unmodified.
I noticed a few days ago that some of the text boxes still allowed invalid data entry. For example, a numeric-only textbox allowed alpha characters, etc. It also displayed a javascript error "Object doesn't support this property or method". I tracked it down to the following function in the jquery.constrain plugin.
function match(item, input, e) {
var arr = item.chars.split("");
for (var i in arr) {
var token = arr[i];
if (token.charCodeAt(0) == e.which) {
return true;
}
}
if (item.regex) {
var re = new RegExp(item.regex);
if (re.test(String.fromCharCode(e.which))) {
return true;
}
}
return false;
};
Debugging through this block of code, I determined the following:
item is an object with two string properties: chars and regex
item.chars is an empty string ("") at the time of failure.
arr, the result of item.chars.split("") is, as expected, an empty array.
Here's where it's weird. Even though arr is an empty array, the for loop assigns a valid value to i. The value is "remove". So we drop into the loop. token is obviously null, because arr["remove"] is null. So token.charCodeAt(0) throws.
I corrected the error by adding an if statement around the for loop, as follows:
if (arr.length > 0) {
for (var i in arr) {
var token = arr[i];
if (token.charCodeAt(0) == e.which) {
return true;
}
}
}
However, I'm completely baffled as to why this was even necessary - is this an IE bug, a bug in the plugin, or am I just holding my breath wrong when I compile the app?
You should never use for(i in arr) to loop over Arrays. If scripts add methods to the Array prototype these too will be iterated using the for(i in arr) loop. This is probably what is causing your errors. You probably have added a script that modifies the Array.prototype chain.
Also read here under "Why you should stop using for…in to iterate (or never take it up)"
http://www.prototypejs.org/api/array

Categories

Resources