Eval vs IF statements (many IF statements) - javascript

This fiddle pretty much explains what I'm looking for. I'm trying to find the simplest way to go about coding something WITHOUT using eval. I can do it without eval but I think I will have to write 1000s of IF statements. Or is there another way?
http://jsfiddle.net/243rz8eq/9/
HTML
Eval way...<br>
Window-A<br>
Window-B<br>
Window-C<br><br>
Non-Eval way. Requires if statement for each window (there will be thousands). Or is there a simpler way I'm not seeing?<br>
Window-A<br>
Window-B<br>
Window-C<br><br>
JavaScript
window.core = {
launch: function(obj_string) {
//we init a blank dhtmlxwindow and do some other things here, then...
var x = eval("new " + obj_string); //fill dhtmlxwindow with proper content
},
launch_no_eval: function(id) {
//we init a blank dhtmlxwindow and do some other things here, then...
if (id==="window_a") var x = wins.a({x:1}); //fill dhtmlxwindow with proper content
else if (id==="window_b") var x = wins.b({x:1}); //fill dhtmlxwindow with proper content
else if (id==="window_c") var x = wins.c({x:1}); //fill dhtmlxwindow with proper content
//and so on for literally thousands of items.
}
};
window.wins = {
a: function(args) {
//this.myName = 'wins.a'; is used for the help topic.
//DB contains columns: [item] / [helpurl]
//Example Data: [wins.a] / [/help/gettingstarted.html]
//That is why in this previous post (http://stackoverflow.com/q/28096922/3112803)
//I was wanting to know if a function could know it's own name so I wouldn't
//have to type this line below for 1000s of items.
this.myName = 'wins.a';
console.log('Window-A is now displayed. Use "'+this.myName+'" to make link to help topic.');
},
b: function(args) {
this.myName = 'wins.b';
console.log('Window-B is now displayed. Use "'+this.myName+'" to make link to help topic.');
},
c: function(args) {
this.myName = 'wins.c';
console.log('Window-C is now displayed. Use "'+this.myName+'" to make link to help topic.');
}
};

Another approach would be to re-write the current usage of accessing object properties via the dot notation and use the bracket notation instead.
launch_no_eval:function(id) {
//we init a blank dhtmlxwindow and do some other things here, then...
if (id==="window_a") var x = wins.a({x:1}); //fill dhtmlxwindow with proper content
else if (id==="window_b") var x = wins.b({x:1}); //fill dhtmlxwindow with proper content
else if (id==="window_c") var x = wins.c({x:1}); //fill dhtmlxwindow with proper content
//and so on for literally thousands of items.
}
could be re-written as follows,
launch_no_eval:function(id) {
var x = wins[id]({x:1});
}
This would mean your HTML markup can change from,
Window-A<br>
Window-B<br>
Window-C<br><br>
to,
Window-A<br>
Window-B<br>
Window-C<br><br>
As Daniel commented below, assuming you have no control over the HTML markup and must use the window_# value being passed in then you could perform the following,
launch_no_eval:function(id) {
// replace 'window_' with empty string to retrieve unique id
var x = wins[id.replace('window_', '')]({x:1});
}

Related

Javascript array changer

How change strings in javascript arrays. I want to change array codes to strings.
How change strings in javascript arrays. I want to change array codes to strings.
How to get this;
var _0x1576 = ["SayHello", "GetCount", "Message : ", "You are welcome."];
function NewObject(_0x7aa7x2) {
var _0x7aa7x3 = 0;
this.SayHello = function (_0x7aa7x4) {
_0x7aa7x3++;
alert(_0x7aa7x2 + _0x7aa7x4);
};
this.GetCount = function () {
return _0x7aa7x3
};
}
var obj = new NewObject("Message : ");
obj.SayHello("You are welcome.");
from;
var _0x1576 = ["SayHello", "GetCount", "Message : ", "You are welcome."];
function NewObject(_0x7aa7x2) {
var _0x7aa7x3 = 0;
this[_0x1576[0]] = function (_0x7aa7x4) {
_0x7aa7x3++;
alert(_0x7aa7x2 + _0x7aa7x4);
};
this[_0x1576[1]] = function () {
return _0x7aa7x3
};
}
var obj = new NewObject(_0x1576[2]);
obj.SayHello(_0x1576[3]);
EDIT: So you have some code, where all the variable names have been replaced by numbers or indices into this global array of names, and you would like to be able to read it. There is already an answer to this question, which contains links to a bunch of useful deobfuscation tools.
Your case here looks fairly trivial - it appears that you could just do a string search and replace, substituting in the array value every time it is indexed. The regexp /_0x1576\[(\d+)\]/g should find everything that accesses the variable _0x1576 with an integer index. The inner group (\d+) should give you the index with which it was found. You could use something like this to do deobfuscate your source. However, some of the names have been lost in the obfuscation process; i.e. the name of the parameter 0x7aa7x4 in the SayHello function can't be restored. You will have to read the method, understand what its' purpose is, and try to come up with a meaningful name yourself.
One question though - just how much code do you have like this? If there are only a few names in the array of strings, then #Nina Scholz's suggestion seems fairly reasonable. Just go through them one by one, in a text editor, and use the 'Find and Replace' functionality.

Script to split JS comma-separated var declaration into multiple var declarations

I have a large codebase with a ton of stuff declared like this:
var x = 1,
y = {
//some object
},
z = function(arg) {
// some function
};
I'd like to run a node script to convert all this to
var x = 1;
var y = {
//some object
};
var z = function(arg) {
// some function
};
It's not as simple as running a regex on it, because as soon as an object or function appears, you can't just look for commas and semicolons anymore.
Is there an existing library or tool which can do this conversion for me? Not looking to minify or uglify the code, I'm just looking to modify existing, human-readable code to get rid of the comma-separated var declarations.
Are there situations where the character sequence , followed by an identifier and then a single = can exist outside of a multiple var declaration? I'm having trouble thinking of one outside of a string literal with those characters, because a single = is used for assignment, and I'm not sure why you'd have a comma before an assignment statement except in the initialization form you're trying to replace.
Granted, it's always risky to use regex in situations where a parser is more appropriate. The pattern would look something like:
,\s*([\$a-z_][\$a-z_0-9]*)(?=\s*=[^=])
Note: The Visual Studio plugin Resharper has a refactoring for this very operation. However, unlike many other refactorings, Resharper does not provide the option to apply this globally.
Maybe I'm missing something, but if all your comma separators lie at the end of a line, you could just use the regex:
replace(/,\n/g, ';\nvar ');
Here's a browser example:
// in node, this would come straight from a file
var string = 'var x = 1,\ny = {\n //some object \n},\nz = function(arg) {\n // some function\n};';
// heres an element we can use for results
var code = document.getElementById('code');
// lets show original string
code.innerHTML = string;
// lets show the new string in a couple of seconds
setTimeout( function () {
// the regex replace
var updated = string.replace(/,\n/g, ';\nvar ');
// updating the code element
code.innerHTML = updated;
// change color to signify finished
code.className = 'done';
}, 2000);
code {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
.done {
background-color: #C0D9AF;
}
<code id="code"></code>
This seems to do the trick:
jsfmt --rewrite "a=b,c=d -> var a=b; var c=d;" input.js > output.js
input.js:
var x = 1,
y = {
//some object
},
z = function(arg) {
// some function
};
output.js:
var x = 1;
var y = {
//some object
};
var z = function(arg) {
// some function
};
using jsfmt

Avoiding having to write the same word over and over again

I'm very new to javascript so this question might sound stupid. But what is the correct syntax of replacing certain words inside variables and functions. For example, I have this function:
function posTelegram(p){
var data = telegramData;
$("#hotspotTelegram").css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$("#hotspotTelegram").hide()
} else {
$("#hotspotTelegram").show()
}
};
There is the word "telegram" repeating a lot and every time I make a new hotspot I'm manually inserting the word to replace "telegram" in each line. What would be a smarter way of writing that code so that I only need to write "telegram" once?
Group similar / related data in to data structures instead of having a variable for each bit.
Cache results of calling jQuery
Use an argument
function posGeneral(p, word){
// Don't have a variable for each of these, make them properties of an object
var data = generalDataThing[word].data;
// Don't search the DOM for the same thing over and over, use a variable
var hotspot = $("#hotspot" + word);
hotspot.css("left", generalDataThing[word].xpos[p] +"px");
if (p < data[0] || p > data[1]) {
hotspot.hide()
} else {
hotspot.show()
}
};
You can't always avoid this kind of repetition (this is general to all programing languages).
Sometimes, you can make generic functions or generic classes, for example a class which would embed all your data :
Thing = function(key, xpos) {
this.$element = $('#hotspot'+key);
this.xpos = xpos;
};
Thing.prototype.pos = function (p, data) {
this.$element.css("left", this.xpos[p] +"px");
if (p < this.data[0] || p > this.data[1]) {
this.$element.hide()
} else {
this.$element.show()
}
};
And we could imagine that this could be called like this :
var telegramThing = new Thing('telegram', xposTelegram);
...
telegramThing.pos(p, data);
But it's really hard to make a more concrete proposition without more information regarding your exact problem.
I recommend you read a little about OOP and javascript, as it may help you make complex programs more clear, simple, and easier to maintain.
For example, using a Thing class here would enable
not defining more than once the "#hotspotTelegram" string in your code
reusing the logic and avoid making the same code with another thing than "telegram"
not having the Thing logic in your main application logic (usually in another Thing.js file)
But don't abstract too much, it would have the opposite effects. And if you don't use objects, try to keep meaningful variable names.
var t = "Telegram";
var $_tg = $('#hotspotTelegram');
$_tg.css("left", "xpos"+t[p] + "px"); // not sure about this line, lol
$_tg.hide();
$_tg.show();
etc.
you can create a selector as variable, something like this
function posTelegram(p){
var data = telegramData;
var $sel = $("#hotspotTelegram");
$sel.css("left", xposTelegram[p] +"px");
if (p < data[0] || p > data[1]) {
$sel.hide()
} else {
$sel.show()
}
};

Working with SVG polygon elements

I'm trying to work with an SVG polygon and javascript. I create a polygon and set its initial point list like this:
var polygon = document.createElementNS('http://www.w3.org/2000/svg','polygon');
polygon.setAttribute("points", "0,0 100,100 200,200");
now what do I do if I want to modify the 2nd point (100,100)? Right now I'm basically reconstructing the whole string again. But can we address "polygon.points" as an array somehow, or is it really just a plain simple string? This can work ok for very simple polygons, but if my polygon eventually has hundreds of point pairs, I'd hate to reconstruct the entire "points" attribute as a string every time I want to modify a single element.
Thanks
You can access the individual point values using the SVG DOM:
var p = polygon.points.getItem(1);
p.x = 150;
p.y = 300;
(Assuming that your UA implements this interface.) See SVGPolygonElement, SVGAnimatedPoints, SVGPointList and SVGPoint.
I find though that using these SVG DOM interfaces (at least for me in Batik, in which I do most of my SVG stuff) is often not faster than just updating the attribute with string manipulation.
No way around it I'm afraid. You have to reconstruct the string again. But it's not difficult to wrap the whole thing in an object, something like:
function Polygon () {
var pointList = [];
this.node = document.createElementNS('http://www.w3.org/2000/svg','polygon');
function build (arg) {
var res = [];
for (var i=0,l=arg.length;i<l;i++) {
res.push(arg[i].join(','));
}
return res.join(' ');
}
this.attribute = function (key,val) {
if (val === undefined) return node.getAttribute(key);
node.setAttribute(key,val);
}
this.getPoint = function (i) {return pointList[i]}
this.setPoint = function (i,x,y) {
pointList[i] = [x,y];
this.attribute('points',build(pointList));
}
this.points = function () {
for (var i=0,l=arguments.length;i<l;i+=2) {
pointList.push([arguments[i],arguments[i+1]]);
}
this.attribute('points',build(pointList));
}
// initialize 'points':
this.points.apply(this,arguments);
}
var polygon = new Polygon(0,0, 100,100, 200,200);
polygon.setPoint(0, 50,10); // set point and automatically re-build points
polygon.points(50,50, 50,100, 200,100); // set everything
polygon.node; // refer to the actual SVG element
* not the best implementation but you get the idea.
You need to use setAttributeNS. You'll probably want to cache that namespace in a variable somewhere so you don't have to keep typing it.
You need to set all points at once, the performance is pretty solid, what you may want to do is manage the array outside and merge it on the setAttribute calls

Why does this function work for literals but not for more complex expressions?

I am having some insidious JavaScript problem that I need help with. I am generating HTML from a JSON structure. The idea is that I should be able to pass a list like:
['b',{'class':'${class_name}'}, ['i', {}, 'Some text goes here']]
...and get (if class_name = 'foo')...
<b class='foo'><i>Some text goes here.</i></b>
I use the following functions:
function replaceVariableSequences(str, vars) {
/* #TODO Compiling two regexes is probably suboptimal. */
var patIdent = /(\$\{\w+\})/; // For identification.
var patExtr = /\$\{(\w+)\}/; // For extraction.
var pieces = str.split(patIdent);
for(var i = 0; i < pieces.length; i++) {
if (matches = pieces[i].match(patExtr)) {
pieces[i] = vars[matches[1]];
}
}
return pieces.join('');
}
function renderLogicalElement(vars, doc) {
if (typeof(doc[0]) == 'string') {
/* Arg represents an element. */
/* First, perform variable substitution on the attribute values. */
if (doc[1] != {}) {
for(var i in doc[1]) {
doc[1][i] = replaceVariableSequences(doc[1][i], vars);
}
}
/* Create element and store in a placeholder variable so you can
append text or nodes later. */
var elementToReturn = createDOM(doc[0], doc[1]);
} else if (isArrayLike(doc[0])) {
/* Arg is a list of elements. */
return map(partial(renderLogicalElement, vars), doc);
}
if (typeof(doc[2]) == 'string') {
/* Arg is literal text used as innerHTML. */
elementToReturn.innerHTML = doc[2];
} else if (isArrayLike(doc[2])) {
/* Arg either (a) represents an element
or (b) represents a list of elements. */
appendChildNodes(elementToReturn, renderLogicalElement(vars, doc[2]));
}
return elementToReturn;
}
This works beautifully sometimes, but not others. Example from the calling code:
/* Correct; Works as expected. */
var siblings = findChildElements($('kv_body'), ['tr']);
var new_id = 4;
appendChildNodes($('kv_body'),
renderLogicalElement({'id': new_id},
templates['kveKeyValue']));
/* Incorrect; Substitutes "0" for the expression instead of the value of
`siblings.length` . */
var siblings = findChildElements($('kv_body'), ['tr']);
var new_id = siblings.length; // Notice change here!
appendChildNodes($('kv_body'),
renderLogicalElement({'id': new_id},
templates['kveKeyValue']));
When I trap out the first argument of renderLogicalElement() using alert(), I see a zero. Why is this?? I feel like it's some JavaScript type thing, possibly having to do with object literals, that I'm not aware of.
Edit: I have this code hooked up to the click event for a button on my page. Each click adds a new row to the <tbody> element whose ID is kv_body. The first time this function is called, siblings is indeed zero. However, once we add a <tr> to the mix, siblings.length evaluates to the proper count, increasing each time we add a <tr>. Sorry for not being clearer!! :)
Thanks in advance for any advice you can give.
If new_id is 0, doesn't it mean that siblings.length is 0? Maybe there is really no sibling.
Perhaps siblings.length is actually 0? Try debugging further (e.g. with Firebug)
OK, I fixed it. As it turns out, I was modifying my source JSON object with the first function call (because in JS you are basically just passing pointers around). I needed to write a copy function that would make a new copy of the relevant data.
http://my.opera.com/GreyWyvern/blog/show.dml/1725165
I ended up removing this as a prototype function and just making a regular old function.

Categories

Resources