views:

80

answers:

3

I need to detect and eval the Javascript code contained in a string.

The following code works, but it only evaluates the first <script>...</script> it founds.

function executeJs(html) {
    var scriptFragment = "<script(.+?)>(.+?)<\/script>";
    match = new RegExp(scriptFragment, "im");
    var matches = html.match(match);
    if (matches.length >= 2) {
        eval(matches[2]);
    }
}

I wonder if there is a method that allows me to iterate and execute all Javascript fragments.

+3  A: 

The reason it only takes the first one is because you're missing the g flag. Try this:

function executeJs(html) {
    var scriptFragment = '<script(.*?)>(.+?)<\/script>';
    var re = new RegExp(scriptFragment, 'gim'), match;
    while ((match = re.exec(html)) != null) {
        eval(match[2]);
    }
}

executeJs('<script>alert("hello")</script>abc<script>alert("world")</script>');
Blixt
Thank you. It works ! I tried with "exec" with no success because I was missing the g flag. One more question if you don't mind, what does that "g" flag means?
Guido
The `g` flag means "global", i.e. that it should search the whole regular expression. It's a performance question, because sometimes you might just want the first match.
Blixt
+1  A: 

Here is some code that does the same thing in a slightly different way. You can pass the string to the function and it will eval all the script tags and return the cleaned source(without script). There is also a slight difference in the way IE handles it, that is handled in the code as well, you may adapt it to your requirements. Also, the evaluated code has the global context. Hope it helps.

function parseScript(_source)
{
   var source = _source;
   var scripts = new Array();

   // Strip out tags
   while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1)
  {
   var s = source.indexOf("<script");
   var s_e = source.indexOf(">", s);
   var e = source.indexOf("</script", s);
   var e_e = source.indexOf(">", e);

   // Add to scripts array
   scripts.push(source.substring(s_e+1, e));
   // Strip from source
   source = source.substring(0, s) + source.substring(e_e+1);
   }

   // Loop through every script collected and eval it
   for(var i=0; i<scripts.length; i++)
  {
   try
      {
       //eval(scripts[i]);
       if(window.execScript)
       {
         window.execScript(scripts[i]); // IE
       }
       else
       {
         window.setTimeout(scripts[i],0); // Changed this from eval() to setTimeout() to get it in Global scope
       }
      }
   catch(ex)
      {
       // do what you want here when a script fails
       alert("Javascript Handler failed interpretation. Even I am wondering why(?)");
      }
   }
   // Return the cleaned source
   return source;
   }
Thiyagaraj
+1: More efficient than using a regular expression, plus it maintains global scope for the executed code.
Blixt
Thanks Blixt, I wrote this code about a year back for an app that eventually died, that explains why some code-time comments are still there. :)
Thiyagaraj
A: 

Blixt should be right...

You may also take a look at prototype's String.evalScripts function.

http://api.prototypejs.org/language/string.html#evalscripts-instance%5Fmethod

Quamis