Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Perl, JavaScript and quoting/escaping

by isync (Hermit)
on Sep 23, 2008 at 17:26 UTC ( [id://713257]=perlquestion: print w/replies, xml ) Need Help??

isync has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I am outputting javascript from perl and as it seems I am running out of quotes - either it brakes perl or javascript... A bit of pseudocode to show the problem:

print "<a href=\"\" onclick=\"this.element.insert('<tr id=\"someid\">< +td><a href=\"\" onclick=\"this.function.test(\'\')\"></a></td></tr>') +\"";


does not work. Any thoughts?

Replies are listed 'Best First'.
Re: Perl, JavaScript and quoting/escaping
by moritz (Cardinal) on Sep 23, 2008 at 17:33 UTC
    From the perl side you can make use of other quoting mechanisms:
    print q{<a href="onclick=some_javascript_function(self)"></a>};

    Just define some_javascript_function() to do what you want.

Re: Perl, JavaScript and quoting/escaping
by ikegami (Patriarch) on Sep 23, 2008 at 20:46 UTC

    Wow! JS in HTML in JS in HTML in Perl!

    (I've broken the lines for readability. It's not quite equivalent with the line breaks, so they should be removed in the final version.)

    Start with the inner JavaScript:

    this.function.test('')

    Add the inner HTML/XHTML:

    <tr id="someid"><td> <a href="" onclick=" this.function.test('') "></a> </td></tr>

    Add the JavaScript:

    this.element.insert(' <tr id="someid"><td> <a href="" onclick=" this.function.test(\'\') "></a> </td></tr> ')

    Add the outer HTML/XHTML: (XHTML requires that "<" be encoded. I encoded ">" as well, though it's not required.)

    <a href="" onclick=" this.element.insert(' &lt;tr id=&quot;someid&quot;&gt;&lt;td&gt; &lt;a href=&quot;&quot; onclick=&quot; this.function.test(\'\') &quot;&gt;&lt;/a&gt; &lt;/td&gt;&lt;/tr&gt; ') ">

    Add the Perl:

    print(" <a href=\"\" onclick=\" this.element.insert(' &lt;tr id=&quot;someid&quot;&gt;&lt;td&gt; &lt;a href=&quot;&quot; onclick=&quot; this.function.test(\\'\\') &quot;&gt;&lt;/a&gt; &lt;/td&gt;&lt;/tr&gt; ') \"> ");

    For comparison purposes, here's what you had:

    print(" <a href=\"\" onclick=\" this.element.insert(' <tr id=\"someid\"><td> <a href=\"\" onclick=\" this.function.test(\'\') \"></a> </td></tr> ') \" ");

    Not even close. It's much better to do it programmatically:

    use HTML::Entities qw( encode_entities ); sub text_to_ht_attr_val { my $s = @_ ? $_[0] : $_; $s = encode_entities($s); return qq{"$s"}; } sub text_to_js_lit { my $s = @_ ? $_[0] : $_; $s =~ s/\\/\\\\/g; $s =~ s/'/\\'/g; # ... return qq{'$s'}; } my $ht_to_insert = q{<tr id="someid"><td>} . q{<a href="" onclick="this.function.test('')"></a>} . q{</td></tr>}; my $js_onclick = 'this.element.insert(' . text_to_js_lit($ht_to_insert) . ')'; my $ht_dyn_ele = '<a href="" onclick=' . text_to_ht_attr_val($js_onclick) . '>'; print( "$ht_dyn_ele\n" );
    <a href="" onclick=" this.element.insert(&#39; &lt;tr id=&quot;someid&quot;&gt;&lt;td&gt; &lt;a href=&quot;&quot; onclick=&quot; this.function.test(\&#39;\&#39;) &quot;&gt;&lt;/a&gt; &lt;/td&gt;&lt;/tr&gt; &#39;) ">

    For comparison purposes, here's what I had:

    <a href="" onclick=" this.element.insert(' &lt;tr id=&quot;someid&quot;&gt;&lt;td&gt; &lt;a href=&quot;&quot; onclick=&quot; this.function.test(\'\') &quot;&gt;&lt;/a&gt; &lt;/td&gt;&lt;/tr&gt; ') ">

    (Note that the single quotes didn't need to be encoded, but encode_entities harmlessly encodes them as "&#39;".)

      That made it clear! (so the entities approach was the right track..).

      Thanks everyone for contributing!
      This is great!  However I have one question.  Is there an alternative to text_to_js_lit packaged as a module?
      
      I'm surprised that (q{\'\'} eq q{\'\'});  wouldn't this be eq q{''}?
      
      In any case the reason I ask is that for testing I'd like to pass perl strings to JS alert().  The problem was at first:
      
      alert("Error: ERROR:  null value in column "bidder_username" violates not-null constraint");  Where I was so lost as to why I couldn't get my string to popup.
      
      Then this:
      alert("Error: ERROR:  null value in column "bidder_username" violates not-null constraint");
      
      Where the result is not readable, this string is displayed as is.
      

        [ Please don't use <pre> here. Start paragraphs with <p>, and wrap computer text (code, input, output, etc) in <c>...</c>. <c>...</c> will even handle escaping "&", "<", ">", "[" and "]" for you. ]

        Is there an alternative to text_to_js_lit packaged as a module?

        package Module; sub text_to_js_lit { my $s = @_ ? $_[0] : $_; $s =~ s/\\/\\\\/g; $s =~ s/'/\\'/g; # ... return qq{'$s'}; } 1;

        I'm surprised that (q{\'\'} eq q{\'\'}); wouldn't this be eq q{''}?

        Say again?

        Where I was so lost as to why I couldn't get my string to popup. [...] Where the result is not readable, this string is displayed as is.

        What? You just said the same piece of code didn't display the message and displayed it as-is.

      This starts it all:
      print '<a href="#" onClick="var xmlHttp = null; xmlHttp = new XMLHttpRequest(); xmlHttp.open( &quot;GET&quot;, &quot;/cgi-bin/bid.js?aid='.( $rBid->{cgi}->param('aid')||-1 ).'&quot; , false ); xmlHttp.send( null ); eval(xmlHttp.responseText); return false;">[Bid]</a> ';

      In the above I'm scared that content from the client is being passed without first being quoted. I should escapeHTML this at the vary least, but this is not enough and whatever function I used needs to be secure enough for the job... Ideally I'd only use something provided by the specific make, model, and version of the processor. Understanding this is a remote web browser I'm willing to make certain concessions... Like using escapeHTML or URL escape functions provided by notable CPAN modules.

      This is where the problem is:
      print $rBid->{session}->header(-type=>'text/javascript'); my $sth = $rBid->{dbh}->prepare( 'INSERT INTO bid VALUES (?, (SELECT bidder_username FROM session WHERE id=?));') or die $rBid->{dbh}->errstr; $sth->execute($rBid->{cgi}->param('aid'), $rBid->{session}->id()||'undef') or print 'alert("Error: '. $rBid->{cgi}->escapeHTML($sth->errstr).'");'."\n"; print 'alert("Bid(s) Placed: '. $rBid->{cgi}->escapeHTML($sth->rows).'");'."\n";
      The escapeHTML above is interpreted by the JS and not the HTML processor, as there is no HTML processor. I could switch to using alert('... but then I'd need to escape '.

        print 'alert("Bid(s) Placed: '. $rBid->{cgi}->escapeHTML($sth->rows).'");'."\n";

        You are generating JavaScript code using something called escapeHTML. Yeah, that's not going to work. Perhaps you should be using the function I posted that generates JavaScript code instead...

Re: Perl, JavaScript and quoting/escaping
by talexb (Chancellor) on Sep 23, 2008 at 19:21 UTC

    You can also use a heredoc:

    print <<HTML; <a href="" onclick="this.element.insert( "<tr id="someid"> <td> <a href="" onclick="this.function.test('')"> </a> </td> </tr>') "> HTML

    This also allows you to make the code readable (as I hope I've done), and therefore identify and fix any mis-matched tags and quotes.

    However, in this example you're using double quotes for the call to insert, then you're also using double quotes for the name of the tr element. That can only lead to heartbreak .. you'll either have to use less quotes or escape some of them. I think the following should work:

    print <<HTML; <a href="" onclick=this.element.insert( "<tr id=someid> <td> <a href='' onclick=this.function.test('')> </a> </td> </tr>")> HTML

    Alex / talexb / Toronto

    "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

      That's some funky tag!
      <a href="" onclick="this.element.insert(" <- Nice JS! <tr <- eh? id="someid" >

      The one from the second snippet is invalid even sooner.

Re: Perl, JavaScript and quoting/escaping
by isync (Hermit) on Sep 23, 2008 at 18:11 UTC
    Actually I solved it half-way: The problem is less the quoting on the perl side, but what ends up in the html output:

    \" becomes " - so inside the href="" we can use the single-quote ' to differentiate things. And if in these single-quotes values need to be presented we double quote \\' so the \' ends up in the html, like example.jsfunction('<a href=\'\'></a>')

    But still I am in trouble if I need to add yet another nested layer of quoted values, for example example.jsfunction('<a href=\'\' onclick=\\'alert('test')\\'></a>') - what shoudl I do with the quotes around test? Any ideas?

    Update:
    I've found out that, at least in Firefox, using entities works: example.jsfunction('<a href=\'\' onclick=\\'alert(&quot;test&quot;)\\'></a>') - but is that portable?
      As the prior poster said: use perl's "bring your own quote characters" to rescue you. Leave ' and " for javascript's feeble mind.
      The entity is &quot;.
        oops - a simple typo in the post, code was ok.
      Generally, as long as you keep using the same type of quote, you double the \'s for each nested level, so try:
      example.jsfunction('<a href=\'\' onclick=\\'alert(\\\\'test\\\\')\\'>< +/a>')

        That would only work for if all of JS, HTML and Perl used "\" to escape. HTML doesn't.

        Assuming you had the right number of slashes — you don't — you're code results in the following HTML:

        <a href='' onclick='alert(\' <- Nice JS! test\')' <- eh? ></a>

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://713257]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (2)
As of 2024-04-25 06:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found