Modules

Once you get started writing Dojo code, you'll start writing more sophisticated client side code. And the more code you write, the more you will split into JavaScript modules. The more files, the more dangerous are the consequences of using JavaScript carelessly. For example, a harmless little function like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function avg(listOfNumbers) {
   sum = 0;
   for(i in listOfNumbers){
      sum += listOfNumbers[i];
   }
   return sum / listOfNumbers.length;
}

tucked away in a script will wreak havoc on the following in another script: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

function sum(listOfNumbers) {
   var retval = 0;
   for(i in listOfNumbers){
       retval += listOfNumbers[i];
   }
   return retval;
}
document.print(avg(a));
document.print(sum(a));

Because the variable sum in the avg function is stored in the global namespace (i.e. it is not prefixed by "var"), it will overwrite the loaded sum() function in the second script. This leads to hard-to-diagnose errors

What you need is to keep these modules corralled in their own spaces. While modularization is not part of JavaScript, Dojo can simulate it for you through namespaces. And you've been using them all along! Whenever you execute a dojo.require, you are invoking a module loaded into its own namespace. So, for example, dijit.Toolbar is a namespace. Java and C# have similar constructs named packages.

While namespaces are optional, they tend to lessen hassles down the road as your code base grows.

What dojo.require Does

The modules bundled with Dojo correspond roughly to .js files underneath the Dojo root. Looking underneath your dojo root directory, you will see at least three top-level directories dojo. dijit. and dojox. Everything that you've dojo.require'd so far has begun with one of these three prefixes.

Like most modern languages, Dojo uses "." to separate modules from submodules from sub-submodules, etc. These correspond to directories, subdirectories and sub-subdirectories underneath the Dojo root. The name after the last period is the JavaScript file name. So for example:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.require("dijit.form.Button");
// Esentially loads the script dijit/form/Button.js underneath the Dojo root

Why not just load the scripts with a SCRIPT tag? Well, besides being shorter, a dojo.require statement ensures that modules are not loaded twice. They also make the build system function better, which you'll see in The Build System chapter. You can, however, pull in any Dojo code after the initial dojo.js script tag with a script tag and the package system will ensure that dependencies for that package are still satisfied. This trick is particularly handy when you need line-number debugging information from some new chunk of code you're working on.

The script dijit/form/Button.js in turn starts like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("dijit.form.Button");
dojo.require("dijit.form._FormWidget");
dojo.require("dijit._Container");
dojo.declare("dijit.form.Button", dijit.form._FormWidget, {
   // [snip]
}
dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
   // [snip]

All of the classes defined in this script are then available to you. By convention, most modules define one class named after the module itself, as in our dijit.form.Button example. You can also use dijit.form.DropDownButton, if you dojo.require("dijit.form.Button"). But dojo.require("dijit.form.DropDownButton") will not work. Each script provides only one package.

Note, however, that it's Dojo convention to prefix "suggested private" classes with a "_". You shouldn't use these classes, even though JavaScript won't throw an error if you do.

Creating Your Own Modules

Easy Way: A Dojo Peer Directory

So suppose you want to create your own module called explosive.space.Modulator. The most straightforward method involves creating dojoroot/explosive/space/Modulator. That way, "explosive" is at the same directory level as "dojo" and "dijit":

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw3 {color: #000066;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .re0 {color: #0000ff;} .geshifilter .re1 {color: #0000ff;} .geshifilter .re2 {color: #0000ff;} .geshifilter .re3 {color: #808080; font-style: italic;} .geshifilter .re4 {color: #0000ff;}
[criecke@smoochie js]$ ls
dijit  dojo  dojox  util
[criecke@smoochie js]$ mkdir --parents explosive/space
[criecke@smoochie js]$ ls
dijit  dojo  dojox  explosive  util

Now create the file "explosive/space/Modulator.js":

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("explosive.space.Modulator");
dojo.declare("explosive.space.Modulator",null,{
    // fil in the body here
});

And you're ready to include it like any other Dojo module:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.require("explosive.space.Modulator");
var eludiumFuel36 = new explosive.space.Modulator();

Sometimes instead of classes, you may want to define plain ol' functions in a module. That's fine too. For example: you could define explosive.space.utilities:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("explosive.space.utilities");
explosive.space.utilities.shuffleOverToCannon = function(steps) {
   // body here
}

Then, after the dojo.require, you can call explosive.space.utilities.shuffleOverToCannon().

Cleaner Way: External Directories

The problem with this approach is your mucking up the dojo root directory. It's better to keep your development separate from Dojo itself. It tends to make source control easier to deal with.

So let's say you put "explosive/space/Modulator.js" underneath private_dojo at the same level as Dojo root. Then you only need to add the registerModulePath statement:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.registerModulePath("explosive","../../private_dojo/explosive");
dojo.require("explosive.space.Modulator");

The module path is specified relative to the /dojoroot/dojo directory.

Enterprise-Class Way: Custom Builds

An alternate method, which doesn't require the registerModulePath, is to have a build script (Ant, Makefile, or whatever) mix both Dojo and your custom stuff under the same root. That way your source code stays separate from Dojo's in development, but mixes together in a nice way for production. See Custom Builds for details.

Module Helpers

Modules for Non-JavaScript Resources

dojo.require() works fine for JavaScript, but what about images, CSS, and other resources? Do you need to access them through absolute paths? Fortunately no. The following code:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var imgNode = document.createElement("img");
imgNode.src = dojo.moduleUrl("explosive.space","images/kaboom.gif");
locates the kaboom.gif file in our module. The nice thing is ... this code will work in any page that has registered the module. It doesn't matter where the code snippet is located relative to kaboom.gif. Slick!

Conditional Inclusion

Sometime modules are dojo.require'd because they may be used. If they are not used, that's a small bit of wasted time. If you know at runtime whether modules need inclusion, you can use dojo.requireIf: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

dojo.requireIf(dojo.isIE, "explosive.space.BlueScreenOfDeathCatcher");