-
Notifications
You must be signed in to change notification settings - Fork 207
The js! plugin may be used to load non-AMD javascript. Since non-modular scripts rely on predefined global variables to be available before they execute, this plugin assists in loading these scripts in order while still loading them as fast as possible in parallel and non-blocking.
(AMD modules do not need to be loaded in order. AMD module loaders, such as curl.js, do automatic dependency management, ensuring that modules are loaded in order.)
I wanted to name this plugin legacy! in stead of js!, but in respect of the several 2005-era javascript libraries out there, I refrained. If you're serious about AMD and/or javascript modules, then you should be using all modules, all of the time.
Actually, there is a very simple way to trick curl.js into thinking almost any
javascript file is an AMD module. Simply place a call to define();
at the end
of non-AMD file. That's it. It's typical, however, that a module return
something to the requester. Consider placing the following code in your
javascript file:
// ...
// up here be code to construct mylib.arrayGoodies
// ...
// at the bottom of the file, we test for AMD and if it's active,
// we define define mylib.arrayGoodies:
if (typeof define == 'function' && define.amd) {
define(mylib.arrayGoodies);
}
You should seriously consider using this "trick" before using the js! plugin. If you are not in control of the non-AMD javascript files or have reasons to leave them unmodified, then keep reading. :)
The js! plugin has two per-resource options:
- !order - forces evaluation of several scripts in the order listed
- !test - ensures the js file loaded by testing for a globally available thing
Not all browsers have the facilities to order scripts. Therefore, the js!
plugin has to use some of it's own tricks. The standard way to order scripts is
via the async="false"
attribute on the script element. The js! plugin will
test for this feature and use it if it is supported.
If async="false"
is not supported, the js! plugin inserts type="text/cache"
instead. Almost all legacy browsers will still load this dummy script despite
not knowing what a "text/cache" script is (dumb, but true, I know, but only
Firefox 3.6 afaik won't load it).
As soon as the dummy script is loaded (but not executed since the browser
doesn't know how!), the script element is deleted. Meanwhile, a queue manager
watches for all of the dummy scripts and replaces them in order with a normal
type="text/javascript"
script element. At this point, the script file is in
the browser's cache (it was loaded by the dummy script element) so the
normal script element loads the script nearly instantaneously.
This is called "prefetching". Yes. It's trickery. But it's fast and it works.
Unless it doesn't work. If you're astute (and I know you are since you're using and/or investigating AMD!), you no doubt noticed that this trick relies on something that is inherently unreliable: the browser's cache.
For most applications, the cache trickery will work great. However, some devs may not be in control of the server's http headers so they cannot assure they are configured correctly. Other devs could be concerned that the files may be too large to be cached in mobile browsers.
Luckily, there's an option for those devs. The js! plugin will look for a
prefetch: false
config option. If found, it will no longer use these tricks
when encountering the !order option. Of course, this also means that the
ordered scripts will no longer load in parallel in legacy browsers, so they will
essentially behave the same as if they were static script elements in the HTML.
You can't have your cake and eat it too (unless all your users are on modern
browsers).
Exmaple usage of prefetch: false
:
curl({
plugins: {
js: {
prefetch: false
}
}
});
This is an experimental feature and may change in a future release.
The !test option is used to ensure a js file loaded in all browsers. IE6-8 and Opera (up to 11.1) don't execute an onerror handler when a script file fails to load. This option helps ensure the loader can catch the error condition, anyways.
Syntax:
"js!myscript.js!test=myapp.someObject.someProperty"
With the above option, the js! plugin will test that
myapp.someObject.someProperty
exists at the global scope. If it doesn't, it
will assume the script did not load and proceed to call any error callbacks
specified in any calls to curl().then()
.
Note: the !test option must be the last option in the resource-id!
Good:
curl(['js!jQuery.js!order!test=$', 'js!myplugin.js!order!test=$.myplugin']);
Bad:
curl(['js!jQuery.js!test=$!order', 'js!myplugin.js!test=$.myplugin!order']);