curl-amd
Version:
curl.js is small, fast, extensible module loader that handles AMD, CommonJS Modules/1.1, CSS, HTML/text, and legacy scripts.
205 lines (184 loc) • 6.22 kB
JavaScript
/** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
/**
* curl legacy loader
*
* Loads legacy javascript scripts as if they were modules. Since legacy
* scripts don't specify any dependencies and typically hoard many
* things into one file, this isn't always straightforward. This loader
* can be configured to adapt to almost any situation.
*
* Config options:
*
* exports {string}
* Typically, specifies the name of a
* global variable exposed by the legacy script, but can be any code that
* can be executed by `eval()` at the global scope. The result of the
* `eval()` is verified not to throw an exception and is used as the value
* exported to other modules that depend on this script.
*
* factory {function}
* The factory is executed when the script
* is loaded and should return something to export to other modules. The
* factory should thow an exception if it can't find the thing to export.
* Even though the `exports` config option can evaluate arbitrary code, the
* `factory` option should be used since it can be tested and/or linted.
* Furthermore, the factory function takes a string argument that identifies
* the module being requested. This allows the function to be reused for
* multiple modules.
*
* NOTE: One of the `exports` or `factory` config options must be provided
* because, without them, there is no way for a loader to determine if a
* script has loaded in IE6-10.
*
* requires {array}
* An array of module ids that are required for the script
* to execute correctly. These module ids may refer to other legacy scripts
* that have been configured via the legacy loader.
*
* dontWrapLegacy {boolean}
* A build-time only option to tell cram to
* add a define() at the end of the script rather than *around* the script.
* Typically, this would never be needed.
*
* @example Backbone
*
* This backbone example uses a function call to return the exports. If
* the code to return the exports is any more sophisticated than this, you
* should consider using a `factory` option instead of `exports` since
* factory functions can be tested and/or linted.
*
* curl.config({
* paths: {
* backbone: {
* location: 'modules/backbone-1.3.1/backbone.js',
* config: {
* loader: 'curl/loader/legacy',
* exports: 'Backbone.noConflict()',
* requires: ['jquery', 'lodash']
* }
* }
* }
* });
*
* @example jQuery UI
*
* This jQuery UI example uses the `factory` option to return the correct
* jQuery UI widget from a concatenated collection of widgets in a
* jqueryui.js file.
*
* curl.config({
* packages: {
* jqueryui: {
* location: 'modules/jquery-1.6.3/jqueryui.js#',
* config: {
* loader: 'curl/loader/legacy',
* factory: function (fullId) {
* var id = fullId.replace('jqueryui/', '');
* return $.fn[id];
* }
* requires: ['jquery', 'css!jqueryui.css']
* }
* }
* }
* });
*/
(function (global, doc, testGlobalVar) {
define(/*=='curl/loader/legacy',==*/ ['curl/_privileged'], function (priv) {
;
var hasAsyncFalse, loadScript, dontAddExtRx;
hasAsyncFalse = doc && doc.createElement('script').async == true;
loadScript = priv['core'].loadScript;
dontAddExtRx = /\?|\.js\b/;
return {
'load': function (resId, require, callback, cfg) {
var exports, factory, deps, dontAddFileExt, url, options, countdown;
exports = cfg['exports'] || cfg.exports;
factory = cfg['factory'] || cfg.factory;
if (!exports && !factory) {
throw new Error('`exports` or `factory` required for legacy: ' + resId);
}
deps = [].concat(cfg['requires'] || cfg.requires || []);
dontAddFileExt = cfg['dontAddFileExt'] || cfg.dontAddFileExt;
dontAddFileExt = dontAddFileExt
? new RegExp(dontAddFileExt)
: dontAddExtRx;
url = require['toUrl'](resId);
if (!dontAddFileExt.test(url)) {
url = nameWithExt(url, 'js');
}
options = {
url: url,
order: true,
// set a fake mimetype if we need to wait and don't support
// script.async=false.
mimetype: hasAsyncFalse || !deps.length ? '' : 'text/cache'
};
// hasAsyncFalse, nodeps: load | _export
// hasAsyncFalse, deps: getDeps+load | _export
// !hasAsyncFalse, nodeps: load | _export
// !hasAsyncFalse, deps: getDeps+load | reload | _export
if (deps.length) {
countdown = 2;
getDeps();
load();
}
else {
countdown = 1;
load();
}
function getDeps () {
// start process of getting deps, then either export or reload
require(deps, hasAsyncFalse ? _export : reload, reject);
}
function load () {
// load script, possibly with a fake mimetype
loadScript(options, _export, reject);
}
function reload () {
// if we faked the mimetype, we need to refetch.
// (hopefully, from cache, if cache headers allow.)
options.mimetype = '';
loadScript(options, _export, reject);
}
function _export () {
var exported;
if (--countdown > 0) return;
if (factory) {
try {
exported = factory.call(global, resId);
}
catch (ex) {
reject(new Error('Factory for legacy ' + resId + ' failed: ' + ex.message));
}
}
else {
try {
exported = testGlobalVar(exports);
}
catch (ex) {
reject(new Error ('Failed to find exports ' + exports + ' for legacy ' + resId));
}
}
// define the module as if it were a regular module.
// Note: the parens hide the "define signature" from cram.js
(define)(resId, exported);
// also return the plugin-syntax module ("legacy!foo").
callback(exported);
}
function reject (ex) {
(callback['error'] || function (ex) { throw ex; })(ex);
}
},
'cramPlugin': '../cram/legacy'
};
function nameWithExt (name, defaultExt) {
return name.substr(name.length - 3) !== '.' + defaultExt
? name + '.' + defaultExt
: name;
}
});
}(
this,
this.document,
function () { return (1, eval)(arguments[0]); }
));