Moritz Angermann

Lazyloading Brushes for dp.SyntaxHighligher using jQuery

When it comes to websites, the most important thing for me is to get the content. So loading content first is always a good idea. Using dp.SyntaxHighlighter means we might eventually load brushes we don’t need before we start to render the code sections. As well as wasting bandwidth.

CC Attribution photo credit: .Jennifer Leigh.
CC Attribution 2.0 photo: credit .Jennifer Leigh.

So the best way would be to only load those brushes you need to style the code sections. A few lines of jQuery supported JavaScript, and we have a solution.

First of all we need a function that makes sure it get’s executes. or dies if it fails too often.

:::javascript
    var n = 10;
    $.failproof_highlight = function() {
        try { 
            dp.SyntaxHighlighter.ClipboardSwf = 'sh/Scripts/clipboard.swf';
            dp.SyntaxHighlighter.HighlightAll('code');
            return true;
        } catch(err) {
            if(n < 1) return false;
            n--;
            t=setTimeout("$.failproof_highlight()",100);
        }
    };

So in case dp.SyntaxHighlighter is not ready yet, we wait for 100ms and try again. Until we succeed or failed for 10 times = 1s.

Next we want to see if we are need to load anything at all. And if so we can at least start loading the stylesheet.

:::javascript
if(pre_code.length) {
    $(document.createElement('link'))
              .attr({type: 'text/css',
                     href: '/sh/Styles/SyntaxHighlighter.css',
                     rel: 'stylesheet',
                     media: 'screen' })
              .appendTo('head');
    var brushMap = {
        python: 'shBrushPython.js',
        py: 'shBrushPython.js',
        xml: 'shBrushXml.js',
        xhtml: 'shBrushXml.js',
        xslt: 'shBrushXml.js',
        html: 'shBrushXml.js',
        xhtml: 'shBrushXml.js',
        js: 'shBrushJScript.js',
        jscript: 'shBrushJScript.js',
        javascript: 'shBrushJScript.js'
    };

Now we only need to figure out what brushes to load. This is pretty straight forward:

:::javascript
    var scripts = ['/sh/Scripts/shCore.js'];
    libs = {}
    pre_code.each(function(i,e) {
        libs['/sh/Scripts/' + brushMap[$(e).attr("class")]] = 1;
    });
    for(lib in libs) { scripts.push(lib); }

And finally we need to load them. We basically spawn requests with the jQuery.getScript function for each file and decrement a counter when the file is loaded. Once all files have been loaded, we call the failproof_highlight function.

:::javascript
    $.script_count = scripts.length;
    $.script_load_notify = function() {
        if(--$.script_count <= 0) $.failproof_highlight();
    };
    for(var i=0; i < scripts.length; i++) {
        $.getScript(scripts[i], $.script_load_notify);
    }

Stitching it together gives us:

:::javascript
$(document).ready(function(){
    var n = 10;
    $.failproof_highlight = function() {
        try { 
            dp.SyntaxHighlighter.ClipboardSwf = 'sh/Scripts/clipboard.swf';
            dp.SyntaxHighlighter.HighlightAll('code');
            return true;
        } catch(err) {
            if(n < 1) return false;
            n--;
            t=setTimeout("$.failproof_highlight()",100);
        }
    };
    {% set base = '/contrib/syntaxhighlighter/' %}
    var pre_code = $("[name=code]")
    if(pre_code.length) {
        $(document.createElement('link'))
                  .attr({type: 'text/css',
                         href: '/sh/Styles/SyntaxHighlighter.css',
                         rel: 'stylesheet',
                         media: 'screen' })
                  .appendTo('head');
        var brushMap = {
            python: 'shBrushPython.js',
            py: 'shBrushPython.js',
            xml: 'shBrushXml.js',
            xhtml: 'shBrushXml.js',
            xslt: 'shBrushXml.js',
            html: 'shBrushXml.js',
            xhtml: 'shBrushXml.js',
            js: 'shBrushJScript.js',
            jscript: 'shBrushJScript.js',
            javascript: 'shBrushJScript.js'
        };

        var scripts = ['/sh/Scripts/shCore.js'];
        libs = {}
        pre_code.each(function(i,e) {
            libs['/sh/Scripts/' + brushMap[$(e).attr("class")]] = 1;
        });
        $.script_count = scripts.length;
        $.script_load_notify = function() {
            if(--$.script_count <= 0) $.failproof_highlight();
        };
        for(var i=0; i < scripts.length; i++) {
            $.getScript(scripts[i], $.script_load_notify);
    }

There is quite a bot of room to improve and maybe even turn this into a jQuery Plugin at some point. I hope this helps someone. Comments appreciated as always.

Moritz Angermann 19 January 2009 Hanoi, Vietnam