1

I am Using Joomla CMS, and I want to manipulate the DOM in render. I want to modify the inline scripts to execute when the page has loaded. But I have problems with a Google maps script that has HTML tags in its code, when I try to get the content of the script using nodeValue, it cuts it off at the first closing of the </div>; I also tried not to modify anything just load the HTML and then save it with saveHTML() and the same problem happens.

Part of page code (page.html)

<!DOCTYPE html>
<html>
<head>
    <title>Page title</title>
    <script src="https://maps.googleapis.com/maps/api/js?key=XXXXXX" type="text/javascript"></script>
</head>
<body>
<header></header>
<main>
    <div class="wrapper">
        <div class="inner">
            <div class="map-container">
                <div class="google-map">
                    <div id="map-canvas95" class="map-canvas" style="width:100%;height:400px">
                        <script type="text/javascript">
                            //API demos Used(synchronous loading, info window,)
                            var myLatlng95 = new google.maps.LatLng(-11.926755,-77.05359);
                            var mapOptions95 = {
                                scrollwheel: false,
                                zoom: 15,
                                center: myLatlng95,
                                disableDefaultUI: true,
                                mapTypeId: google.maps.MapTypeId.ROADMAP,
                                mapTypeControl: true,
                                mapTypeControlOptions: {
                                    style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
                                    position: google.maps.ControlPosition.LEFT_TOP
                                },
                                panControl: true,
                                panControlOptions: {
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                },
                                zoomControl: true,
                                zoomControlOptions: {
                                    style: google.maps.ZoomControlStyle.SMALL,
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                },
                                scaleControl: true,
                                streetViewControl: true,
                                streetViewControlOptions: {
                                    position: google.maps.ControlPosition.LEFT_CENTER
                                }
                            };
                            var map95 = new google.maps.Map(document.getElementById('map-canvas95'), mapOptions95);

                            //Info Window
                            var contentString95 = '<div id="content">'+
                                '<div id="siteNotice"></div>'+
                                '<h1 id="firstHeading" class="firstHeading">Market Title</h1>'+
                                '<div id="bodyContent">'+
                                    '<p>Graphic Design. Lorem ipsum dolor sit amet, consectetur adipiscing elit. of Quisque ultricies vestibulum molestie.</p>'+
                                '</div>'+
                            '</div>';
                            var infowindow95 = new google.maps.InfoWindow({
                                content: contentString95,
                                maxWidth: 300
                            });
                            
                            //Marker
                            var marker95 = new google.maps.Marker({
                                position: myLatlng95,
                                map: map95,
                                title: 'Market Title'           });

                            //Event for open Info Window
                            google.maps.event.addListener(marker95, 'click', function() {
                                infowindow95.open(map95,marker95);
                            });
                        </script>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>
<footer></footer>
</body>
</html>

My DOM Script

    //$app = JFactory::getApplication('site'); for Joomla only
    //$body = $app->getBody(false);//for Joomla only
    $body = file_get_contents('page.html');
    $domDoc = new DOMdocument();
    libxml_use_internal_errors(true);
    $domDoc->loadHTML($body);
    libxml_clear_errors();
    $scripts =$domDoc->getElementsByTagName('script');
    $openScript = "\nwindow.addEventListener('DOMContentLoaded', function() {";
    $closeScript = "});\n";
    foreach ($scripts as $script) {
        $newScript = $openScript.$script->nodeValue.$closeScript;
        $script->nodeValue = (!empty($script->nodeValue)) ? $newScript : $script->nodeValue;
    }
    //$body = $domDoc->saveHTML();  for Joomla  
    //$app->setBody($body);for Joomla
    $domDoc->saveHTMLFIle('newDom.html');

In result file you can see script cutted

<script>
  window.addEventListener('DOMContentLoaded', function() {    
  var contentString<?php echo $uniqid; ?> = '<div id="content">'+
  '<div id="siteNotice"></div>;
  });
</script>

Like this Google maps script, I have seen other scripts that have HTML tags in their code. How do I solve this?. Thanks.

Leoalv
  • 25
  • 8
  • 1
    At least in HTML 4.01 times, the general recommendation was that any `` occurring within the content of a script element, should be escaped as `<\/`, to avoid any parser problems. I am guessing the occurrence of `` trips the PHP DOM parser up as well here. _“I want to modify the inline scripts to execute when the page has loaded.”_ - can you explain which way exactly you are doing that? The code indicates that you are trying to “clone” the script element, but what are you then eventually doing with it? – CBroe Nov 27 '20 at 08:11
  • @CBroe Thanks for your answer, I have added more details to the code and also the result to better understand what I am trying to do. It works well with almost all scripts except those with HTML tags. The Google maps code does not belong to me, it is an extension. – Leoalv Nov 27 '20 at 14:51
  • @Leo so that I am sure that I am testing on correct input data, can I have the `$body` value? This is important to the [mcve]. – mickmackusa Nov 28 '20 at 13:52
  • @mickmackusa I have updated the code to be able to test directly from PHP, without the need for Joomla. – Leoalv Nov 28 '20 at 18:48
  • Maybe a different DOM parser library could be an option. https://stackoverflow.com/questions/4029341/dom-parser-that-allows-html5-style-in-script-tag – CBroe Nov 30 '20 at 07:26

1 Answers1

2

HTML5 is not supported by ext/dom. You can use the HTML5-PHP library. It reads the HTML5 into an XHTML DOM.

use \Masterminds\HTML5;

$html5 = new HTML5();
$document = $html5->loadHTML(getHTML());
$xpath = new DOMXpath($document);
// register namespace prefix for Xpath expressions 
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');

foreach ($xpath->evaluate('//xhtml:div[@class="google-map"]//xhtml:script') as $script) {
    $script->textContent = "\nwindow.addEventListener('DOMContentLoaded', function() {\n{$script->textContent}\n});\n";
}
echo $html5->saveHTML($document);

Also, do not use DOMNode::$nodeValue but DOMNode::$textContent to ensure proper handling of special characters.

ThW
  • 19,120
  • 3
  • 22
  • 44
  • Thanks, I will read more about this, because I also need to add attributes to the images and links. – Leoalv Dec 02 '20 at 17:52
  • Is there an advantage in using `evaluate()` versus `query()` in this case? @ThW – mickmackusa Dec 02 '20 at 22:51
  • Not for this expression. I just never use `query()` because it does not support all expressions. (Only node list results, not scalars). – ThW Dec 03 '20 at 02:25