SyntaxHighlighter: Easier to load; faster to boot?

SyntaxHighlighter has a relatively high surface area, typically requiring two CSS files and at least two JavaScript files to be linked into a web page. Here’s a truncated example:

<html>
<head>
	<!-- Stylesheets for SyntaxHighlighter -->
<link type="text/css" rel="stylesheet" href="styles/shCore.css"/>
<link type="text/css" rel="stylesheet" href="styles/shThemeDefault.css"/>
...
</head>
<body>

<em>...Some kind of interesting page content usually goes here, 
but we're not interested in that right now...</em>

	<!-- Load the SyntaxHighlighter Core and Brush scripts. A 
               separate Brush script is required for each language. -->
<script type="text/javascript" src="scripts/shCore.js"></script>
<script type="text/javascript" src="scripts/shBrushJava.js"></script>
<script type="text/javascript" src="scripts/shBrushJScript.js"></script>
	<!-- Don't forget the XML brush if you're using html-script -->
<script type="text/javascript" src="scripts/shBrushXml.js"></script>

	<!-- Now that all the script files are loaded, begin highlighting. -->
<script type="text/javascript">
SyntaxHighlighter.config.stripBrs = true;
// ... additional configuration, as needed ...

SyntaxHighlighter.all();
</script>

</body>

Some users have wanted a simpler way to integrate SyntaxHighlighter into their sites. Last year, David Chambers wrote a script that uses the Prototype library to load the required CSS and brush files. He ran into a troubling quirk in SyntaxHighlighter: It needs any required brush files to be loaded before it starts highlighting. I responded by updating the OOWB fork of SyntaxHighlighter, so that brushes could be loaded asynchronously. But that’s as far as I’d gone towards automated loading, until now.

I recently looked into adding automated loading to SyntaxHighlighter itself, coming up with not one, but two automated solutions. I should really say one and a half, as one is still in the alpha stage and will likely stay there.

The “alpha-stage” approach comprises a new JavaScript file that loads SyntaxHighlighter and the necessary brush files through AJAX, by way of a pair of simple Java servlets. I’d hoped that this would lead to faster load times, by reducing the total number of downloads. And so it does—sometimes. For testing, I used Google App Engine as the servlet host (using the free quotas.) Performance was inconsistent, to say the least; this is probably due to the Google App Engine’s method of starting up and shutting down servlets. It seemed that only by making repeated hits on the servlets in rapid succession could I be sure of getting to a container that already had my servlets running, thus getting good load times.1 The servlets are still up there; if you’d like the Java source (to host them elsewhere), and/or the JavaScript for using them, just let me know.

I also implemented a pure JavaScript approach, which I hadn’t expected to yield better load times. Yet I’ve seen a number of cases where this is faster than the original, static way of loading SyntaxHighlighter. I’m releasing a new download so that you can try it out. I highly recommend that you test it thoroughly before putting it into real use.

To use this new method, the web page invokes SyntaxHighlighter.boot instead of SyntaxHighlighter.all. When boot is used as the entry point, SyntaxHighlighter determines which brushes are required by the web page, and issues HTTP requests for each brush file (as well as the appropriate CSS.) These requests use the age-old method of adding a tag for each brush, and a tag for each CSS file.

Using SyntaxHighlighter.boot, a web page only needs to add a couple of scripts, usually at the end of the . No CSS link elements are required.

<!--  We just need to load shCore.js near the end of the <body> element,
	then call SyntaxHighlighter.boot() with our configuration options. -->

<script type="text/javascript"
src="http://path/to/sh/directory/syntaxhighlighter/scripts/shCore.js"></script>

<script type="text/javascript">
	SyntaxHighlighter.boot(
		"http://path/to/syntaxhighlighter/root/syntaxhighlighter/",
		{theme : "Default"}, // Configuration settings
		{stripBrs : true}      // Default settings
	);
</script>

</body>

That’s it. Every page on your site can use the same set-up, regardless of which brush files it needs. (For another example, look at the HTML source for this page.)

When an HTML page includes external scripts—that is, elements with src attributes, the browser loads the script files synchronously, as it encounters them in the HTML. When the scripts are loaded dynamically, the browser can load them asynchronously; several scripts may then be loading simultaneously. This can be particularly helpful for pages that use multiple brushes. Here’s a Firebug sequence diagram of a web page with five brushes, loading in SyntaxHighlighter’s traditional way:

(Click on image for larger view)

Here’s the same page, modified to use the boot method. Notice that, unlike in the first picture, the shBrush...js files are loaded in parallel:

(Click on image for larger view)

Of course, this isn’t an entirely new discovery, but it came as a pleasant surprise nonetheless.

Availability

In addition to SyntaxHighlighter itself, I also modified Viper007Bond’s SyntaxHighlighter Evolved plugin for WordPress to use the new boot method. This simplified the code quite a bit, though it may have introduced bugs.

SyntaxHighlighter and SyntaxHighlighter Evolved are each available on the downloads page. Test before using. Note: If you’ve been using SyntaxHighlighter Evolved, you will probably need to re-set your settings if you install this plugin.

Implementation notes

In the traditional SyntaxHighlighter, all brushes must be loaded before SyntaxHighlighter.all is invoked. In this fork, “>brushes can load at any time after shCore.js has been loaded. This flexibility was key to the design of the boot method: If a brush is loaded after SyntaxHighlighter.all or SyntaxHighlighter.boot is invoked, SyntaxHighlighter looks through the page to see if there’s any input requesting the newly loaded brush.

The boot method needs to map each brush name to the script file that implements that brush. I implemented this by modifying the Perl script that builds and stages SyntaxHighlighter. The script now parses the *Brush.js files in the source tree, pulling out the names and aliases supported by each file, and writes them into an array in shCore.js. The downside of this is that testing via the boot method requires re-running the script, which builds and stages the files into a new directory, from where I can run tests. Even though the script only takes about a second, it’s crossed the boundary between having no build step in the development cycle, and having any build step in the development cycle.

When You Come To A Fork In The Road, Take It.

I had two motives for working on SyntaxHighlighter: One, to improve its display of code on my own blog and elsewhere. Two, as a training ground for learning JavaScript.

I was between jobs when I took on this project. As of this Monday, that will no longer be the case. That’s great news for me, but much as I’d like to keep the SyntaxHighlighter work moving forward, I don’t really know if or when I’ll have the time for it. On the other hand, I no longer feel like I need a private JavaScript training ground. So, I’ve pushed my repository to bitbucket.org as a fork of Alex’s repository. If he so chooses (and so far he hasn’t, which is fine), Alex is welcome to merge the fork into his branch. Perhaps more importantly, anyone else can now fork the code, and/or make contributions to this fork.

I do have some more goals in mind for SyntaxHighlighter. I’ll be writing about them in the not-too-distant future.


1Perhaps a faster host would lead to the faster load times I’d hoped for; but that would probably require payment, which makes this approach considerably less attractive.

,

4 Comments

Leave a Reply

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