Login Register

Yojo: Loading YUI with the Dojo loader

I like Dojo 0.9, and I think it has a great solution to loading JS modules dynamically. The core of the Dojo loader has been there since the beginning of the project, but with the addition of the xdomain loader, it really shines (disclaimer: I work on the xdomain loader). It allows anyone to use all of Dojo from a CDN without having to download it.

The loader is a great fit for any JavaScript library that has more than a couple of files to load, and where the files can have dependencies on other files. I touched on this topic during my Ajax Experience talk about Dojo's xdomain loader. This post explores a real-world example: using the Dojo loader to load YUI.

First, a demo. Here is a page that loads the YUI rich-text Editor using the Dojo loader. If you have a DOM inspector tool, you can verify that the modules where loaded by looking at the dynamically created script elements inside the head element.

How was it done?

  1. I started with the 2.3.0 YUI source that Jonathan Bond-Caron modified to put in calls for dojo.require/provide (but he used a different namespace name). Jonathan opted for using module names for the dojo.require/provide calls, but I converted them to just be the file names so that I could avoid having to do a mapping (configuration) step.
  2. I converted directories and file names with dashes to use underscores. Since they are used as the dojo.provide() resource name, they have to be valid JavaScript object identifiers (no dashes in JS variable names).
  3. I did a modification to base.js to make sure the right YAHOO object was used -- there is a scope difference when a file is built as an xdomain file via the Dojo build system.
  4. Converted the test page to include the Dojo loader and use dojo.require/provide calls instead of script tags.
  5. Run a Dojo custom build that generated xdomain .xd.js files for the YUI modules. I also just built the Dojo loader and not all of the things that normally come in the basic dojo.js.

The biggest step was specifying each file's dependencies by putting dojo.require() calls in each JS file. Thankfully, Jonathan had done the work already.

File size impact

Here are the file size comparisons (sizes are gzip sizes in bytes):

  • 3697 yuiloader-beta-min.js (YUI's beta loader)
  • 5632 dojo.js (Use this if you are loading all code from same domain)
  • 8950 dojo.xd.js (Use this for loading code across domains)

(The demo above is *not* using gzipped or optimized files, so its file sizes will be different)

dojo.js/dojo.xd.js also includes things mentioned in this post (see Basic loader section), like

  • DOMContentLoaded/dojo.addOnLoad()/dojo.addOnUnload()
  • dojo.get/setObject
  • djConfig setup
  • a basic XMLHttpRequest creation function.

dojo.xd.js also includes dojo.i18n. I have a couple of ideas on reducing the size gap between dojo.xd.js and dojo.js.

Comparing with YUI Loader

YUI does have a beta code loader, yuiloader. However, I do not feel it is sufficient for programming in the large, and for keeping encapsulation. I am not fully conversant with the yuiloader, so I'm happy to make any corrections to my comparison below.

Configuration vs. Convention

yuiloader uses configuration over convention, which means you need to register with the yuiloader a name for your JS module, where to find it and its dependencies. In the long run, this is a more verbose approach than using a convention. In Dojo, you register a base path for a "namespace", then a simple convention is used to convert "path.to.resource" to [namespace prefix]/path/to/resource.js (or .xd.js in the xdomain loading case).

This results in less code when specifying dependencies. If you are an end user of the modules, you do not have to worry about mapping out all the dependencies yourself.

Breaks encapsulation

Since you need to specify all dependencies up front, you break encapsulation. You have to know about a module's dependencies before using it and you have to update the dependency list registered with yuiloader if the module changes.

Build optimizations

Not a problem for yuiloader directly, but the Dojo build system can analyze the dojo.provide()/require() mappings in the modules so you can build a custom "rollup" file that includes all the modules you need in one file.

Different loading profile

From what I can tell, yuiloader only loads one file at a time, and waits until it is loaded before loading the next one, so it can avoid issues with something loading before a dependency is loaded. The Dojo xdomain loader starts appending script tags as soon as it knows it needs to load them, waits for them all to load, then defines the modules in the right order. Dojo can do this because the build process wraps the module code in a function, so that the code does not execute when the script is first loaded.

i18n

Dojo has a neat way to do internationalization (i18n) by using JSON localization (l10n) bundles. These bundles can be optimized during the Dojo build process just like normal JS modules.

yuiloader advantages

yuiloader has some nice features missing from today's Dojo loader:
- Can load any code on the net, and does not require a build step, given the caveats above. This is possible with dojo.io.script, but that is not part of the Dojo loader, so it requires extra work by the developer.
- You can load CSS files. I would like to support a "dojo.requireCss()" that would give you this functionality and still leverage the convention-based name-to-path mapping that Dojo uses.

Next Steps

I would like to see YUI and Dojo converge on using the same type of loader for their code. There is a generic need for this functionality, and it would be good to start standardizing on an approach.

Obviously I would like to start with the Dojo loader and the dojo.provide/require approach and make any modifications that YUI might need to fulfill their needs. I am sure the Dojo loader code could use improvements, in particular the xdomain loader. Look at the file. Isn't that scary? It scares me and I wrote it! Let's see if we can make it better.

I am also open to other paths if Dojo's loader does not seem like the way to go. However, I would like some solution that keeps encapsulation and can leverage a build process for optimizations.

This is also an important test case for further YUI/Dojo collaborations. We are solving lots of the same issues, and I think we will serve the Open Web better if we work together rather than separately.

I am sure that Dojo would love to have some YUI people as Dojo contributors. Yahoo has already been generous in allowing Dojo to use their YUI Reset CSS rules in our "reset CSS". And Dojo's very liberal open source license allows YUI to pick up any piece from Dojo. I am looking forward with working with the YUI team on issues that affect both YUI and Dojo.

By the way, this invitation to work on a common loader is not just for YUI: I know jQuery is looking for something in this area, and I can see where Ext would benefit from it too. Let's try to work out a solution together. Feel free to contact me at jburke at dojotoolkit dot org. Feel free to start the conversation by just listing your loader requirements.

The demo does not run on FF nor IE

Hi,

The demo is crashing with the folloawing message : uncaught exception: Could not load cross-domain resources: YAHOO.container.container YAHOO.menu.menu YAHOO.button.button_beta YAHOO.editor.editor_beta

Best regards

Seems OK to me

I just tried with Firefox 2.0.0.6 on OSX and IE 6 on Windows XP. I cleared cache before trying the URLs. It sounds like maybe your browser could not find dojotoolkit.org (the domain that has the YUI files)? Maybe a temporary server glitch. Seems fine now.

Had the same problem...

...worked after clearing the cache.

base.js

Hi James,

I'm writing up a full tutorial on my blog to help people that don't understand the Loader as in-depth, be able to get parts of YUI loading through Dojo. I have a question in regards to your base.js...This is the code you have in the base.js before the dojo.provide(); statement...

if(typeof window.dojo == "undefined") {
var dojo = {};
dojo.provide = function(resourceId) {};
dojo.require = function(resourceId, /*optional*/resourceType) {};
dojo.isYahooStub = true;
}

When I have this code in there, the parser blows up when trying to load the base.js file. If I take it out however, it seems to function perfectly. Is there a reason behind having this code in there? I can see that it would be useful if the dojo object didn't exist yet, or the provide and require statements, but since we're using a dojo.require() for the YUI libraries, that means we already have these objects and functions available to us anyways.

Any thoughts, suggestions, comments? Just trying to make sure everything in my tutorial is clear to the viewers. :)

Thanks!
Justin

You are correct, the code is

You are correct, the code is in there so that base.js could be used in an environment without Dojo.

I'm curious to know what the error you are getting. What is the error and in what browser? You mention the parser but what parser are you referring to? The browser's javascript parser?

This test page still seems to work at least in Firefox 2.

base.js

Ah yes, I suppose you would need those objects and functions around if you were to take YUI and use it stand-alone after...Never even thought of that ;)

Anyways, the error occurs when the loader in Dojo 1.0.1 goes to use the eval function, in IE6 and IE7, (whether it be a custom-eval provided by the user or the browser's default eval) in the dojo._loadUri function. The line that triggered it was:

var value = d["eval"](contents+"\r\n//@ sourceURL="+uri);

Which results in 'dojo' is null or not an object.

I almost wonder if it's related to this comment in the dojo.js.uncompressed.js...

// FIXME: investigate Joseph Smarr's technique for IE:
  //		http://josephsmarr.com/2007/01/31/fixing-eval-to-use-global-scope-in-ie/
  //	see also:
  // 		http://trac.dojotoolkit.org/ticket/744

Firefox 2.0 seems to work fine...I can test others if you need me to (Safari 2.0, Safari 3.0, etc.)

Thanks again!

Justin

Ah, OK, then this hopefully

Ah, OK, then this hopefully should work for the code that goes in Yahoo's base.js:

if(typeof window.dojo == "undefined") {
window.dojo = {};
window.dojo.provide = function(resourceId) {};
window.dojo.require = function(resourceId, /*optional*/resourceType) {};
window.dojo.isYahooStub = true;
}

Thanks!

Bingo! That works great :)

Thanks for taking the time to look into this!