-1

I need to do some in-attribute JavaScript replacement to add custom JavaScript to the attribute. To be specific, to add a JS confirm() function around all of it. A rather hacky thing, but a thing I have to do regardless.

Here is the HTML tag I need to replace.

<input type='submit' id='gform_submit_button_4' class='gform_button button' value='Send'  onclick='/* Lots of JS */' onkeypress='/* Lots of JS */' />

I have succeeded in doing it with the following PHP code.

$new_submit_html = $submit_html;
// __() is WordPress's function for internationalized text
$confirm_text = __("It will not be possible to modify your responses anymore if you continue.\\n\\nAre you sure you want to continue?", 'bonestheme');
$new_js_start = 'if( window.confirm("' . $confirm_text . '") ) { ';
$new_js_end = ' } else { event.preventDefault(); }';

$new_submit_html = preg_replace_callback( "/(onclick|onkeypress)(=')([^']*)(')/", function( $matches ) use( $new_js_start, $new_js_end ) {
    $return_val = $matches[1] . $matches[2] . $new_js_start . $matches[3] . $new_js_end . $matches[4];
    // (Other irrelevant manipulations)
    return $return_val;
}, $new_submit_html );

return $new_submit_html;

This works like a charm right now, because the JavaScript where I wrote "Lots of JS" just so happens not to contain \' -- escaped single quotes -- which it could definitely contain.

I've seen this question, which would allow me to match the apostrophe unless it's escaped, but I'm not sure how to reverse it to match anything but an unescaped apostrophe. I imagine the solution will include lookbehinds, but I'm not sure how to proceed in this exact case.

Ariane
  • 393
  • 4
  • 14
  • It would be much more elegant to attach the handler properly using Javascript rather than an inline attribute, that way no escaping is necessary (see `addEventListener`) – CertainPerformance Mar 05 '19 at 22:35
  • @CertainPerformance Agreed (and that was my first reflex), but the only way I could find for this to work with this weirdly-coded plugin was play within its attribute-JS territory – Ariane Mar 05 '19 at 22:38

1 Answers1

1

I would use DOMDocument to do this as it won't care about the actual contents of the attribute as long as they are already valid:

function wrap_js($js) {
    $confirm_text = "It will not be possible to modify your responses anymore if you continue.\\n\\nAre you sure you want to continue?";
    $new_js_start = 'if( window.confirm("' . $confirm_text . '") ) { ';
    $new_js_end = ' } else { event.preventDefault(); }';
    return $new_js_start . $js . $new_js_end;
}
$html = "<input type='submit' id='gform_submit_button_4' class='gform_button button' value='Envoyer'  onclick='/* Lots of JS */' onkeypress='/* Lots of JS */' />";
$doc = new DOMDocument();
$doc->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($doc);
foreach ($xpath->query("//input[@type='submit']") as $submit_input) {
    foreach (['onclick', 'onkeypress'] as $attribute) {
        if (($js = $submit_input->getAttribute($attribute)) != '') {
            $submit_input->setAttribute($attribute, wrap_js($js));
        }
    }
}
echo $doc->saveHTML();

Output:

<input type="submit"
       id="gform_submit_button_4"
       class="gform_button button" 
       value="Envoyer"
       onclick='if( window.confirm("It will not be possible to modify your responses anymore if you continue.\n\nAre you sure you want to continue?") ) { /* Lots of JS */ } else { event.preventDefault(); }'
       onkeypress='if( window.confirm("It will not be possible to modify your responses anymore if you continue.\n\nAre you sure you want to continue?") ) { /* Lots of JS */ } else { event.preventDefault(); }'
>

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
  • I don't know why I didn't think of this. Great idea, and simply eliminates the worry of weird/complex regex. – Ariane Mar 05 '19 at 22:42
  • 1
    @Ariane indeed. Most times I think of using regex to parse HTML I go and read [this](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags?rq=1) :-) – Nick Mar 05 '19 at 22:47
  • 1
    If anyone tried the above and it messed up special characters, try this : https://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly – Ariane Mar 06 '19 at 14:38