I read a thread somewhere about how great it would be if SyntaxHighlighter would only load the brush scripts that it actually needed. In case you hadn’t noticed, there are about 16 different brushes that can be loaded with SyntaxHighlighter depending on which brushes you might need. The problem is that everyone of these brushes you choose are loaded whether you need them or not on that page. So connecting to the default page, where you may not have any code showing, would still load all of the brushes. Yuck.
I started thinking how easy this should be able to handle using the magic of jQuery. Well it seems like it should have been easy… As it turns out loading JavaScript dynamically can be a little puzzling for some people, especially me. Lets have a look at how I accomplished this task and you can see for yourself how easy it was…
Let’s start by loading the shCore.js file, the SyntaxHighlighter.css file, and defining the clipboard.swf flash file:
$j = jQuery.noConflict();
$j(document).ready(function() {
var shBasePath = 'dp.SyntaxHighlighter/';
var shScriptPath = '/js.axd?path=' + shBasePath + 'Scripts/';
$j.getScript(shScriptPath + 'shCore.js', function(){
dp.SyntaxHighlighter.ClipboardSwf = shScriptPath + 'clipboard.swf';
$j('head').append('<link href="/css.axd?name=' + shBasePath + 'Styles/SyntaxHighlighter.css" rel="stylesheet" type="text/css" />');
...
};
})
Thanks to jQuery getScript we can load the script and then peform additional functionality if it loaded correctly.
Next I needed to determine what brush scripts were needed. Again, jQuery makes this pretty easy using the following syntax:
if($j('pre.xml').length > 0) {shAddScript('shBrushXml.js')};
Be warned that you might be tempted to not include the ‘.length > 0’ portion of the syntax, but it won’t work. I found this out the hard way…
So working through all of the brush class filters should load only the list of brush scripts that need to be loaded, right? Not so fast! First you have to perform this after the DOM has loaded, or you won’t be able to see which brushes you need. So we wrap the set of calls inside a document.ready function:
function shAddScript(source){
$j.getScript(shScriptPath + source, function(){
alert(source + " loaded");
})
};
But if we do that then the javascripts that are dynamically loading won’t be executed as part of the page load, will it? No, it doesn’t (again, found this out the hard way). So I created a new JavaScript file called highlightall.js in the script folder that simply executes the dp.SyntaxHighlighter.HighlightAll('code') function. Since jQuery is loading and executing the script the highlighting job gets done.
You might also notice that I have an alert in there to tell me when a script is loaded. I have this in there to during testing so that I can see when one actually loads. When I roll this out to my production server I will comment out that line, as I would expect you would want to also.
So now we have only the necessary scripts loading. We should be all set, right? Well, I wish...
One particular problem surfaced with how the SyntaxHighlighter WLW Plug-In uses the string ‘c#’ as the class name for C# code. In other words, when you insert C# code the class name is c#. For some reason jQuery was never able to locate the element using the standard $j(‘pre.c#’) call. So after thinking about it I decided to dynamically replace any found c# class names with csharp.
$j("pre").each( function () {
// For each <pre> tag check if the class name is c# and replace it with csharp
$j(this).attr('class', $j(this).attr('class').replace(new RegExp("c\#", "g"),'csharp') ); }
);
<gloat> You have to love jQuery for this kind of functionality! </gloat>
So this turned <pre class=”c#”> to <pre class=”csharp”> and then jQuery could quickly locate all instances of this element. If anyone knows a better way to do this, or how to make jQuery locate a class with the # included in the name please share with me!
Well that’s about it. I wrapped this all up into it’s own JavaScript file so it doesn’t clutter up your site.master page. Just copy the script to your website and add the script tag in your head section:
<script src="/js.axd?path=js/jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="/js.axd?path=js/jQuery.syntaxHighlight.js" type="text/javascript"></script>
With that you should be good to go. To validate the expected behavior I used fiddler2 to watch the traffic. Sure enough only the necessary brush scripts were loaded. In fact, even the core isn’t loaded if there aren’t any <pre> tags… Very nice…
Don’t forget to download and copy to your website the SyntaxHighlighter scripts. I placed them in ~/dp.syntaxhighter but you should be able to place them anywhere. Also you need to download and copy to your website (or link to Google’s source page) the jQuery core script. Also, don’t forget to create/copy the highlightall.js script into the same folder as your dp.SyntaxHighlighter scripts. Lastly don’t forget to add the Windows Live Writer SyntaxHighlighter Plug-in so you can really take advantage of this code! Check out my previous post for more details about the WLW Plug-in.
UPDATE - Sept 27th: I was tipped off to a blunder in the flow if this script. It seems that an additional code panel would be created each time the dp.SyntaxHighlighter.HighlightAll('code') was executed. So when I had a page with two different brush scripts the second brush to load would cause the first brush to generate an additional panel. Not so good… A bolt of lightning came to me on how to fix this. I just created a new JavaScript called highlightall.js, added it to the same place as the core and brush scripts, and inserted a single command: dp.SyntaxHighlighter.HighlightAll('code'). I have updated this post to reflect the changes.
As always, if you have any problems feel free to drop me a line and let’s work on it together.
jQuery.syntaxHighlight.js
HighlightAll.js