Setting up

Before you can use the JS.require() function, you need the following loaded into the page:

You can get JS.packages either by loading the package.js or loader.js into your environment. (loader.js is package.js plus dependency data for jsclass itself; if you just want to use JS.packages to manage your code without using the rest of the jsclass library, use package.js.)

After loading the package manager, you need to tell it where to find your code.

The package manifest

To describe your packages, you list the external script files used by your application, stating which JavaScript objects are provided by the file and which objects it depends on. For example, here are a few modules from the JS.Class library:

JS.packages(function() { with(this) {
    file(JSCLASS_PATH + '/core.js')
        .provides('JS.Module',
                  'JS.Class',
                  'JS.Kernel');

    file(JSCLASS_PATH + '/comparable.js')
        .provides('JS.Comparable')
        .requires('JS.Module');

    file(JSCLASS_PATH + '/enumerable.js')
        .provides('JS.Enumerable')
        .requires('JS.Module',
                  'JS.Class');

    file(JSCLASS_PATH + '/hash.js')
        .provides('JS.Hash',
                  'JS.OrderedHash')
        .requires('JS.Class',
                  'JS.Enumerable',
                  'JS.Comparable');
}});

Notice how Enumerable depends on Class and Module, which are both provided by the same file, core.js. Similarly, Hash requires Enumerable and Comparable, which both have a dependency on Module. The package manager resolves all this and only loads each file once. Object detection is used to figure out whether each file has been loaded and a file is not requested unless some of its objects appear to be missing.

Where possible, the package system will attempt to load scripts in parallel where it spots that execution order doesn’t matter. For example, Enumerable and Comparable do not depend on each other, so when we want to load Hash we can load these dependencies in parallel. If the load order of a set of scripts is important, you must make sure you make this clear using the requires() statement. A file will not be loaded until all the objects it requires are present.

A package can also list multiple files. In this case, because JS.packages doesn’t know anything about the relationships between the files, it cannot automatically parallelize downloads and will just download them sequentially when you call JS.require(). This is provided as a convenience for loading libraries composed of multiple files, where listing their interdependencies is more trouble than it’s worth and you’d rather specify the load order youself.

For example, to load the Fancybox library, you could do this:

JS.packages(function() { with(this) {
    file( 'fancybox/lib/jquery-1.7.1.min.js',
          'fancybox/source/jquery.fancybox.pack.js',
          'fancybox/source/helpers/jquery.fancybox-buttons.js',
          'fancybox/source/helpers/jquery.fancybox-thumbs.js')
        .provides('jQuery.fancybox');
}});

In addition to requires(), there is a statement called uses() that specifies a ‘soft dependency’, i.e. an object the package needs but that does not necessarily need to be loaded first. For example, Set uses a Hash for storage but you could load Hash after the Set package just fine. On the other hand, Set mixes in Enumerable and this must be loaded before Set is defined. And, Hash is itself based on Enumerable. So the package config for this looks like:

JS.packages(function() { with(this) {
    file(JSCLASS_PATH + '/enumerable.js')
        .provides('JS.Enumerable');

    file(JSCLASS_PATH + '/hash.js')
        .provides('JS.Hash')
        .requires('JS.Enumerable');

    file(JSCLASS_PATH + '/set.js')
        .provides('JS.Set')
        .requires('JS.Enumerable')
        .uses('JS.Hash');
});

The advantage of using uses() is that it helps the package system optimise the downloading of packages, since if the load order does not matter the packages can be downloaded in parallel.