External javascript files in Magento

During the development of my latest Magento shop I ran into the problem of not being able to include external javascript files as easily as you would for internal ones.

The problem:

Just as a quick recap, to include a javascript file in your shop header you just need to add a line to your page.xml file (app>design>frontend>default>yourthemename>layout):

<action method="addJs"><script>../skin/frontend/default/platinumenv/js/functions.js</script></action>

But if you change the path above by something starting with http or https, Magento will not recognize it as being external and try to append the base js url to it. For example a line like this:

<action method="addJs"><script>http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js</script></action>

will outpout something like that in the header:

<script type="text/javascript" src="http://www.myshop.com/js/http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>

You can of course hard code the script tag but 1) it will have to be before or after the files included by the xml, which might create some conflicts and 2) it’s not fun at all!

My idea of a solution

After digging into some of the core files responsible for the output of the js I started to have a better idea of how to resolve the issue. The good thing about Magento is that when a core file does not behave the way you want, you can “easily” override.

The file we want to change here is Mage/Page/Block/Html/Head.php. This is where Magento handles inclusions in the header as well as file merging for js and css, among other stuff.

I am going to override this class to change one of its function: &_prepareStaticAndSkinElements(). Here is the new code:

<?php
/**
 * Extjs
 * Override core class to provide a way to include external javascript files
 */
class Foo_Page_Block_Html_Head extends Mage_Page_Block_Html_Head
{
   

    /**
     * Same as core file exept first foreach loop where we look for http or https in the
     * url of the script
     */
    protected function &_prepareStaticAndSkinElements($format, array $staticItems, array $skinItems, $mergeCallback = null)
    {
        $designPackage = Mage::getDesign();
        $baseJsUrl = Mage::getBaseUrl('js');
        $items = array();
        if ($mergeCallback && !is_callable($mergeCallback)) {
            $mergeCallback = null;
        }

        // get static files from the js folder, no need in lookups
        foreach ($staticItems as $params => $rows) {
            foreach ($rows as $name) {
                //if http or htttps, do not add baseurl, do not try to merge
                if(strstr($name, 'http://') || strstr($name, 'https://'))
                    $items[$params][] = $name;
                else
                    $items[$params][] = $mergeCallback ? Mage::getBaseDir() . DS . 'js' . DS . $name : $baseJsUrl . $name;
            }
        }

        // lookup each file basing on current theme configuration
        foreach ($skinItems as $params => $rows) {
            foreach ($rows as $name) {
                $items[$params][] = $mergeCallback ? $designPackage->getFilename($name, array('_type' => 'skin'))
                    : $designPackage->getSkinUrl($name, array());
            }
        }

        $html = '';
        foreach ($items as $params => $rows) {
            // attempt to merge
            $mergedUrl = false;
            if ($mergeCallback) {
                $mergedUrl = call_user_func($mergeCallback, $rows);
            }
            // render elements
            $params = trim($params);
            $params = $params ? ' ' . $params : '';
            if ($mergedUrl) {
                $html .= sprintf($format, $mergedUrl, $params);
            } else {
                foreach ($rows as $src) {
                    $html .= sprintf($format, $src, $params);
                }
            }
        }
        return $html;
    }
} 

Very simple, if the path has http:// or https:// then do not include the baseurl :)

You then need to declare the new class in etc/local.xml. Add those lines between the existing <global> tags:

<blocks>
      <page>
            <rewrite>
                  <html_head>Foo_Page_Block_Html_Head</html_head>
            </rewrite>
      </page>
</blocks> 

And that’s it. You can now enjoy adding external javascript files to your xml.

This entry was posted in Blog. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

8 Responses to External javascript files in Magento

  1. Julie Cheung says:

    Ace post! I like the presentation of the problem and solution. This may well come in useful.. ^_^

  2. Nellie says:

    Hmm, I don’t have an etc/local.xml.

    I have a layout/local.xml

    Would that be it?

    • Ben says:

      The etc/local.xml is where db details are stored so you definitelly have one. The full path to it is root/app/etc/local.xml.

      I’ve never seen a layout/local.xml so can’t really say if it will do the same. Would be nice if it did though.

  3. mm says:

    Can’t believe there is no easier way to do it.
    Seems weird!

    But I appreciate it anyway – Thx.

  4. Mahesh says:

    It’s really helpful for include external javascripts and speed up the magento site.

    Regards,
    Mahesh

  5. Shahid says:

    Awesome! thanks a bunch man…

  6. Jason says:

    or you could save yourself the hassle and use setText in the head reference.

    • Ben says:

      At the time of writing this solution was the only one I could find but if you say you can simply add a setText now them all the better :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • Flickr Photostream

    • Wood beam
    • Door frame
    • Coloured tiles
    • Heads up
    • In the doorway
    • Courtyard
    • Ceilling
    • Marrakesh Atlas mountains
    • Orange tree
    • Fountain
    • Bougainvillier
    • Garden