2

I'm receiving some 'body' content from a jquery's json call, where I can get the unique javascript element returned by doing:

script_element = $(data.body)[1]

This equals to:

<script type=​"text/​javascript">​
    updater('foo', 'bar', {}, '0', constant='');
</script>​

So, typeof script_element returns "object"

And, if I run script_element.innerText, I can get:

updater('foo', 'bar', {}, '0', constant='');

After receiving this script, what I'm doing right now is just run an eval on it, but searching around I couldn't get a way to run eval changing function call params.

What I'm trying to do is change the third param of the call, in this case the {}, that can change depending on the return of the json call, so I can't just search for {}.

I could also do script_element.text.split(',')[2] for example, and change this text on the fly, but I was thinking there should be a better way to do this.

I don't know if javascript can recognize and treat a "future method call", but still think there should be a better way.

Any idea?

Gabriel L. Oliveira
  • 3,922
  • 6
  • 31
  • 40
  • Really, there is no "more beautiful" ways different from that you know :) – RReverser Jul 18 '11 at 17:06
  • @RReverser I was thinking there should be something like `new FunctionCall` (as exists `new Function`), and then a `.changeParam(2, 'new_value')`. This would be AWESOME! Or, just a better way then that `.split` – Gabriel L. Oliveira Jul 18 '11 at 17:07
  • 1
    If you can change what is being returned by your server-side code, you should. Embedding JavaScript in HTML being sent of the wire in JSON is **probably** a bit much. Better to send over the data structures you need and then use JavaScript to build the HTML you need on the client side (and then you can make calls to your functions with the proper parameters.) – Sean Vieira Jul 18 '11 at 17:09

4 Answers4

5

What you could do is shadowing the function so as to be able to alter the third argument. You ought to define that shadowing function before fetching the JSON.

var originalUpdater = updater; // keep old function to call

// overwrite (shadowing)
updater = function(a, b, c, d, e) {
    // change c appropriately here
    originalUpdater(a, b, c, d, e);
}

Then you can still just eval it (which is not very safe, but that's not your point if I'm not mistaking), and it will call the shadow function.


A more generic shadowing method would be along the lines of:

var originalUpdater = updater; // keep old function to call

// overwrite (shadowing)
updater = function() {
    // change arguments[2] appropriately here
    originalUpdater.apply(this, arguments);
}

Fiddle: http://jsfiddle.net/n7dLX/

pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • I would have posted the same - with the caveat that I'd probably wrap the whole business in a (function(){})() wrapper so as not to expose 'originalUpdater' to the global scope. Also, you might want to restore 'updater' after the call - but that's application specific. – jimbo Jul 18 '11 at 17:11
  • I got what you mean, and maybe will do that, but I was hoping there would be a even better way. This one orders me to have a last param, so that I can pass wether or not to change it, and how to generate the new value or receive it. Anyway, thanks to answer. I'll wait more responses, and if don't get a better one, will accept yours as the better one. :) – Gabriel L. Oliveira Jul 18 '11 at 17:11
  • @Gabriel L. Oliveira I think @pimvdb is right, that's really tricky but nice solution :) – RReverser Jul 18 '11 at 17:19
  • @Gabriel L. Oliveira: Could you tell me what exactly do you mean with 'orders me to have a last param'? – pimvdb Jul 18 '11 at 17:21
  • Becase I was thinking in something generic, such that I can apply different behaviours on different parts of events. In this case, I'd apply an `if` into this wrapper function, or move the outside logic into it. Although, thanks for this answer. – Gabriel L. Oliveira Jul 18 '11 at 17:33
  • @pimvdb Thank you for this one again. I'll use it as to change future behaviours along with some event binds. Thanks. – Gabriel L. Oliveira Jul 18 '11 at 17:52
  • @pimvdb This is really awesome. Give more possibilities – Gabriel L. Oliveira Jul 19 '11 at 15:02
  • @pimvdb Forget what I told with "order me to have a last param". Now, I'm overriding the `updater` function just when some events are thrown, and this way I can just treat the params as I want. Thank you so much! – Gabriel L. Oliveira Jul 19 '11 at 16:04
3

Change the server. Rather than returning

<script type=​"text/​javascript">​
    updater('foo', 'bar', {}, '0', constant='');
</script>​

Return

{
  "method": "updater",
  "params": [
    "foo", "bar", {}, "0", ''
  ]
}
pimvdb
  • 151,816
  • 78
  • 307
  • 352
Raynos
  • 166,823
  • 56
  • 351
  • 396
2

Assuming that you cannot change what is being sent over from the server, you can simply run through the innerText with a regular expression and pass update the HTML before you insert it.

var replacer = /\w+\(([^()]+)\)/gi;
script_element.innerText.replace(replacer, function(matched_text, func_params){
    var orig_func_params = func_params;
    // Make changes to func_params here.
    return matched_text.replace(orig_func_params, func_params);
});

This can be functionized by doing the following:

var replacer = /\w+\(([^()]+)\)/gi;
function replace_arg(script_element, arg_index, replacement_value) {
    script_element.innerHTML = script_element.innerHTML.replace(replacer, 
    function(matched_text, func_params){
        var orig_func_params = func_params;
        func_params = func_params.split(",");
        if (arg_index >= func_params.length) {
             throw new RangeError(arg_index + " is out of range. Total args in function:" + func_params.length);
        }
        func_params[arg_index] = JSON.stringify(replacement_value);
        return matched_text.replace(orig_func_params, func_params.join(","));
    });
return script_element;
}

This can be called in this way:

script_element = replace_arg(script_element, 3, {"new":"arg"});
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
0

I don't understand what you are doing, but in general if you don't want to rely on the order of parameters make the function take one parameter that is an object whose properties are the parameters:

function add(params) {
    var a = params.hasOwnProperty("paramA") ? params.paramA : 0;
    var b = params.hasOwnProperty("paramB") ? params.paramB : 0;
    return a + b;
}

add({paramA: 1, paramB: 2});

In this case you should use hasOwnProperty to check if the function was passed the parameter you are looking for before trying to access it.

nwellcome
  • 2,279
  • 15
  • 23