Skip to content

Instantly share code, notes, and snippets.

@hereswhatidid
Last active December 11, 2019 08:50
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hereswhatidid/8c8edef106ee95138b03 to your computer and use it in GitHub Desktop.
Save hereswhatidid/8c8edef106ee95138b03 to your computer and use it in GitHub Desktop.
PrestaShop Media class override to allow for forcing some inline JavaScripts to remain inline.
<p>Some HTML goes here</p>
<script type="text/javascript" data-keepinline="true">
// this script will remain here when rendered
alert( "hello!" );
</script>
<script type="text/javascript">
// this script will be forced to the bottom of the page
alert( "hello again!" );
</script>
<?php
Class Media extends MediaCore
{
public static function deferScript($matches)
{
if (!is_array($matches))
return false;
$inline = '';
if (isset($matches[0]))
$original = trim($matches[0]);
if (isset($matches[1]))
$inline = trim($matches[1]);
/* This is an inline script, add its content to inline scripts stack then remove it from content */
if (!empty($inline) && preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $original) !== 0 && Media::$inline_script[] = $inline)
return '';
/* This is an external script, if it already belongs to js_files then remove it from content */
preg_match('/src\s*=\s*["\']?([^"\']*)[^>]/ims', $original, $results);
if (isset($results[1]) && (in_array($results[1], Context::getContext()->controller->js_files) || in_array($results[1], Media::$inline_script_src)))
return '';
/* return original string because no match was found */
return $original;
}
}
@nicooprat
Copy link

Works great, thank you so much !

To install :

  1. Paste the file to override/classes/Media.php
  2. In cache/class_index.php find and complete this block :
'Media' => 
  array (
    'path' => 'override/classes/Media.php',
    'type' => 'class',
  )

@nicooprat
Copy link

You could also add this part && preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $original) !== 0 to the end of the last if statement to allow external javascript links in place, like :

<script src="/themes/mytheme/js/myscript.js" data-keepinline="true"></script>

Not sure it's the best way but it doest the job. This can be useful or necessary sometimes (for example with Modernizr or HTML5shiv).

Prestashop should not decide for us, and you really should propose a pull request !

@kewix
Copy link

kewix commented Mar 10, 2015

This just saved me hours of headache ! Thanks :)

@jleonrp
Copy link

jleonrp commented Mar 17, 2015

Thanks a lot @hereswhatidid! I was lost and searching for a solution and found yours.
It Works perfectly.

@kesalp
Copy link

kesalp commented Apr 18, 2015

My extended solution to work on external scripts too. With @nicooprat version, an extra (duplicated) script include was created.

Class Media extends MediaCore
{
    public static function deferScript($matches)
    {
        if (!is_array($matches))
            return false;
        $inline = '';

        if (isset($matches[0]))
            $original = trim($matches[0]);

        if (isset($matches[1]))
            $inline = trim($matches[1]);

        /* This is an inline script, add its content to inline scripts stack then remove it from content */
        if (!empty($inline) && preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $original) !== 0 && Media::$inline_script[] = $inline)
            return '';

        /* This is an external script, if it already belongs to js_files then remove it from content */
        preg_match('/src\s*=\s*["\']?([^"\']*)[^>]/ims', $original, $results);
        if (array_key_exists(1, $results))
        {
            if (substr($results[1], 0, 2) == '//')
            {
                $protocol_link = Tools::getCurrentUrlProtocolPrefix();
                $results[1] = $protocol_link.ltrim($results[1], '/');
            }
            if (preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $original) !== 0) {
                if (in_array($results[1], Context::getContext()->controller->js_files) || in_array($results[1], Media::$inline_script_src))
                    return '';
            } else {
                Context::getContext()->controller->removeJS($results[1]);
            }
        }

        /* return original string because no match was found */
        return $original;
    }
}

@mma87
Copy link

mma87 commented Apr 29, 2015

thanks for this solution!
I try with a test code and works fine.
I try also adding data-keepinline to modules/ganalytics/views/templates/hook/header.tpl file with no success.

have you any solutions?

@desire2create
Copy link

@programmer85g Thanks for the duplicated code, that works like a charm!

@sophia-s
Copy link

Hats off to the source.....solved my problems was dragging since a month long .

Copy link

ghost commented Jul 20, 2015

That worked fine until 1.6.0.14. Can't make it work in 1.6.1.0...
Did you manage to get this working in this version?
Thanks!

@pgumiela
Copy link

I found it very helpful on PrestaShop 1.6.0.8. Thank you.

@michaelhjulskov
Copy link

Thank You very much - this almost solved all my problems.
I still have an issue though (tag manager telling me "Missing line breaks may cause issues.")
when I turn on compress javascript, the minify/compress function alters the content inside the script tag.
Im looking for a way to avoid that any changes at all is made inside script. I tried using literal, didnt work.

@michaelhjulskov
Copy link

I made an approach.
But there are some issues with it.
among them is that // become //
resulting in error from tag manager "Missing CDATA comments."

public static function packJSinHTML($html_content)
{
    if (strlen($html_content) > 0)
    {
        // Michael Hjulskov
        // This is an inline script, add its content to inline scripts stack then remove it from content
        if (preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $html_content) == 0)
            return $html_content;
        // end Michael Hjulskov
        return parent::packJSinHTML($html_content);
    }
    return false;
}

@testers3
Copy link

hi, may i know the correct way of inserting the file?
1.) paste the media.php file to override/classes.
2.) paste the following code to cache/class_index.php
'Media' =>
array (
'path' => 'override/classes/Media.php',
'type' => 'class',
)

Problem: i cannot find cache/class_index.php
I only have cache/index.php file

Do we need to place example.tpl to anywhere?

Hope some guru can advice a little. thanks

@gRoussac
Copy link

gRoussac commented Sep 1, 2015

Hi,

Thank you for your gist and sorry for late reply

I made this PR https://github.com/PrestaShop/PrestaShop/pull/3875/files that should enable your recommandation.

Regards

@javiBertos
Copy link

If you want to prevent to compress a block of JS code to prevent problems (f.e. google retargeting code), I've added this code to the Media override class:

public static function packJSinHTMLpregCallback($preg_matches)
  {
    if (!(trim($preg_matches[2])))
      return $preg_matches[0];

      $preg_matches[1] = $preg_matches[1].'/* <![CDATA[ */';
    if (preg_match('/<\s*script.*(data-nocompress)[^>]*>/ims', $preg_matches[1]) !== 0) {
      $preg_matches[2] = preg_replace('/\n?\s*\/\*\s*\*\/\n?/ims', "\n", $preg_matches[2]);
    } else {
      $preg_matches[2] = Media::packJS($preg_matches[2]);
    }
      $preg_matches[count($preg_matches) - 1] = '/* ]]> */'.$preg_matches[count($preg_matches) - 1];

    unset($preg_matches[0]);

    $output = implode('', $preg_matches);
    return $output;
  }

And in your script line, you must add data-nocompress="true"

I hope it would be helpful for you.

Regards!

@patrykmarek
Copy link

hello gRoussac there is one problem with Your commit original code written by hereswhatidid do keep-inline as well for external js code and that was important for code like conversion sale form gogole, i developed many modules which i sell in addons marketplace and now their are not working at all because of that changes you made :-) can you add as well to keep-inline work on external files.

@Hop-Seb
Copy link

Hop-Seb commented Sep 7, 2016

Hi everybody !

I am using prestashop 1.6.1.4. And these codes don't work for me.

The most important for me it's this code :

public static function packJSinHTMLpregCallback($preg_matches)
  {
    if (!(trim($preg_matches[2])))
      return $preg_matches[0];

      $preg_matches[1] = $preg_matches[1].'/* <![CDATA[ */';
    if (preg_match('/<\s*script.*(data-nocompress)[^>]*>/ims', $preg_matches[1]) !== 0) {
      $preg_matches[2] = preg_replace('/\n?\s*\/\*\s*\*\/\n?/ims', "\n", $preg_matches[2]);
    } else {
      $preg_matches[2] = Media::packJS($preg_matches[2]);
    }
      $preg_matches[count($preg_matches) - 1] = '/* ]]> */'.$preg_matches[count($preg_matches) - 1];

    unset($preg_matches[0]);

    $output = implode('', $preg_matches);
    return $output;
  }

By

javiBertos

I have overrided the Media class and deleted the index cache but when I add a script like Google Tag Manager or another, it doesn't work.
When I want to call the function I do :

<script data-nocompress="true" >
[...]
</script>

May I have help please !

Thank you so much...

@jayasilen
Copy link

Hi,

With regards to the script below, is it necessary? Also how do we know that the script already belongs to js_files? Which file/folder should we refer to?

/* This is an external script, if it already belongs to js_files then remove it from content */ preg_match('/src\s*=\s*["\']?([^"\']*)[^>]/ims', $original, $results); if (array_key_exists(1, $results)) { if (substr($results[1], 0, 2) == '//') { $protocol_link = Tools::getCurrentUrlProtocolPrefix(); $results[1] = $protocol_link.ltrim($results[1], '/'); } if (preg_match('/<\s*script(?!.*data-keepinline)[^>]*>/ims', $original) !== 0) { if (in_array($results[1], Context::getContext()->controller->js_files) || in_array($results[1], Media::$inline_script_src)) return ''; } else { Context::getContext()->controller->removeJS($results[1]); } }

@mikemania
Copy link

mikemania commented Apr 13, 2017

In PS 1.6.1.11 it seems a set of empty script tags are created at the end for me.

<script type="text/javascript">
<script>
</script>

@Alkarin
Copy link

Alkarin commented Sep 20, 2017

I can not seem to find override/classes/Media.php in my wordpress project.
Is this directory specific to the prestashop theme? As I'm having this issue with the Scalia theme, and can not find where to place this code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment