Release Notes

Test Suite Passes

Browsers tested and passing the Test Suite (please help to fill in gaps):
Windows: IE (6, 7, 8), FF (1.5, 2, 3, 3.5, 3.6rc1), Chrome 4, Opera (9.6, 10), Safari 4, Netscape (7.2, 8.1, 9), Mozilla 1.7
Mac (OSX): Safari (3.2, 4), Netscape 9, FF (1.5, 2, 3, 3.5), Opera (9.6, 10), Mozilla 1.7, Camino 2
Linux: FF (1.5, 2, 3.5), Opera (9.5, 9.6, 10), Netscape 9

Known unsupported

Safari 2, Opera 8, Konquerer (3.4, 4.1)


Documentation

Old and busted:

<script src="framework.js"></script>
<script src="plugin.framework.js"></script>
<script src="myplugin.framework.js"></script>
<script src="init.js"></script>

New hotness:

<script>
   $LAB
   .script("framework.js").wait()
   .script("plugin.framework.js")
   .script("myplugin.framework.js").wait()
   .script("init.js");
</script>

In the above example, all scripts load in parallel (by default). "framework.js" needs to execute first, before the others. But "plugin.framework.js" and "myplugin.framework.js" have no dependencies between them, so their execution order is not important (first-come, first-served). "init.js" needs to wait for all 3 scripts to execute before it runs.

There are a few other variations on the .script(...) signature. For instance, you don't have to do a single script() call for each file (though I think it makes thing more readable). You can pass as many scripts singularly as parameters to one script() call. You can also pass an array of scripts, and it will loop through them and load them in the same way. Lastly, you can pass in an object instead of string, and the object literal can contain "src", "type", and "charset" specifications, if you need to override the defaults (for instance, load a "text/vbscript" script).

How to deal with inline scripts

So, there's some special gotchas you need to look out for when using LABjs not only to load scripts but also to "couple" inline scripts to execute in the proper order with the loaded scripts. Take this expanded example from above:

<script src="framework.js"></script>
<script src="plugin.framework.js"></script>
<script src="myplugin.framework.js"></script>
<script>
   myplugin.init();
</script>
<script>
   framework.init();
   framework.doSomething();
</script>

In this example, the browser would normally make sure that the two inline script blocks at the end will not even be parsed/executed until after the 4 scripts have been loaded. So, the references to "myplugin.init", "framework.init", and "framework.doSomething" will already be guaranteed to be defined (in the scripts that are loaded before the code is parsed).

But, because LABjs is an asynchronous loader, this "guarantee" will not be true. So, this code will fail:

<script>
   $LAB
   .script("framework.js").wait()
   .script("plugin.framework.js")
   .script("myplugin.framework.js")
</script>
<script>
   myplugin.init();
</script>
<script>
   framework.init();
   framework.doSomething();
</script>

The reason is because "myplugin.init", "framework.init", and "framework.doSomething" will be undefined symbols the instant after the $LAB chain first executes and starts loading the scripts. To make this code asynchronous safe, we do this instead:

<script>
   $LAB
   .script("framework.js").wait()
   .script("plugin.framework.js")
   .script("myplugin.framework.js")
   .wait(function(){
      myplugin.init();
      framework.init();
      framework.doSomething();
   });
</script>

We wrap the inline script code in a ".wait(function(){ ... })" wrapper, which will defer its execution until the proper time in the loading order of the chain.

A simple pattern for converting <script src="..."></script> and <script>... /* inline code */ ...</script> tags into $LAB API calls, use these two rules:

  1. For every <script src="..."></script> tag you are replacing, you should have a ".script(...)" call
  2. For every <script>... /*inline code*/ ...</script> inline script block with code in it, we need a ".wait(function(){ ... })" call to wrap around the code

If this still is a little confusing, it's ok! Read more about converting inline synchronous code to LABjs suitable asynchronous code over on getiblog.

Special "Feature" Note

LABjs is able to fork it's behavior between browsers because of the use of some small but fairly solid browser inference sniffs. This is different from feature-detection (which is always preferable when possible), but with script-loading quirk behavior, there's no way to feature-detect, we simply have to fork behavior based on browser family. So, the following two browser inference sniffs are in the code at this point:


LABjs API

Methods



void $LAB.setGlobalDefaults(object optionsObject)

Sets one or more options as global defaults to be used by all $LAB chains on the page. This method is not chainable, and thus must only be called stand-alone like this: $LAB.setGlobalDefaults({...});

Parameters
optionsObject : an object which contains name/value pairs for the options to set:

Returns
none


[$LAB] $LAB.setOptions(object optionsObject)

Sets one or more options only to be in effect for the current $LAB chain being executed. This method is chainable (in fact, for it to have any effect, it must be part of a chain!), but it must be the first call in the chain (as changing many of the options mid-chain would create race conditions). So, it must be called like this: $LAB.setOptions({...}).script(...)...;

Parameters
optionsObject : an object which contains name/value pairs for the options to set
Returns
$LAB : the chained object reference so subsequent calls to script() and wait() can be made.


[$LAB] $LAB.script(varies,...)

This method accepts one or more parameters of varying types. Each parameter value is intended to specify a script resource URI to load.

Parameters
varies (can be any number/combination of the following):

Returns
$LAB : the chained object reference so subsequent calls to script() and wait() can be made.


[$LAB] $LAB.wait(function inlineScript[=null])

This function serves two purposes. Firstly, when inserted into a chain, it tells the chain to ensure that all scripts previously listed in the chain should finish executing before allowing the internal 'execution cursor' to proceed to the remaining part of the chain. Essentially, you can specify "blocking" behavior in terms of execution (not loading!) to ensure dependencies execute in the proper order (regardless of how they were loaded).

Secondly, you can specify a function reference (usually an inline anonymous function) that will be executed in order, immediately following the chain's previous scripts executing, but before subsequent scripts in the chain are executed. This is synonymous with inline <script> tags before or after <script src="..."> type tags.

For instance, it can be used to defer the initialization execution of a script you are downloading, like this: $LAB.script("script.js").wait(function(){initScript();}); where initScript() is a function that is defined in "script.js".

Parameters
inlineScript : (optional, defaults to null)
Returns
$LAB : the chained object reference so subsequent calls to script() and wait() can be made.


Examples

Example 1:

	$LAB
	.script("script1.js")
	.script("script2.js")
	.script("script3.js")
	.wait(function(){ // wait for all scripts to execute first
		script1Func();
		script2Func();
		script3Func();
	});


Example 2:

	$LAB	
	.script({ src: "script1.js", type: "text/javascript" })
	.script("script2.js")
	.script("script3.js")
	.wait(function(){ // wait for all scripts to execute first
		script1Func();
		script2Func();
		script3Func();
	});


Example 3:

	$LAB
	.script("script1.js", "script2.js", "script3.js")
	.wait(function(){ // wait for all scripts to execute first
		script1Func();
		script2Func();
		script3Func();
	});


Example 4:
	
	$LAB
	.script( [ "script1.js", "script2.js" ], "script3.js")
	.wait(function(){ // wait for all scripts to execute first
		script1Func();
		script2Func();
		script3Func();
	});
	
	
Example 5:

	$LAB
	.script("script1.js").wait() // empty wait() simply ensures execution order be preserved for this script
	.script("script2.js") // both script2 and script3 depend on script1 being executed before
	.script("script3.js").wait() // but are not dependent on each other and can execute in any order
	.script("script4.js") // depends on script1, script2 and script3 being loaded before
	.wait(function(){script4Func();});

Example 6:

	$LAB
	.script("script1.js") // script1, script2, and script3 do not depend on each other, 
	.script("script2.js") // so execute in any order
	.script("script3.js")
	.wait(function(){  // can still have executable wait(...) functions if you need to
		alert("Scripts 1-3 are loaded!");
	})
	.script("script4.js") // depends on script1, script2 and script3 being executed before
	.wait(function(){script4Func();});
	
Example 7:

	$LAB
	.setOptions({AlwaysPreserveOrder:true}) // tells this chain to implicitly "wait" on 
	                                        // execution (not loading) between each script
	.script("script1.js") // script1, script2, script3, and script4 *DO* depend on each 
	.script("script2.js") // other, and will execute serially in order after all 4 have have
	.script("script3.js") // loaded in parallel
	.script("script4.js")
	.wait(function(){script4Func();});