webui-aria2/js/libs/dojo/_base/loader.js.uncompressed.js
2012-05-01 19:52:07 +08:00

673 lines
26 KiB
JavaScript

//>>built
define("dojo/_base/loader", ["./kernel", "../has", "require", "module", "./json", "./lang", "./array"], function(dojo, has, require, thisModule, json, lang, array) {
// module:
// dojo/_base/lader
// summary:
// This module defines the v1.x synchronous loader API.
// signal the loader in sync mode...
//>>pure-amd
if (!1){
console.error("cannot load the Dojo v1.x loader with a foreign loader");
return 0;
}
var makeErrorToken = function(id){
return {src:thisModule.id, id:id};
},
slashName = function(name){
return name.replace(/\./g, "/");
},
buildDetectRe = /\/\/>>built/,
dojoRequireCallbacks = [],
dojoRequireModuleStack = [],
dojoRequirePlugin = function(mid, require, loaded){
dojoRequireCallbacks.push(loaded);
array.forEach(mid.split(","), function(mid){
var module = getModule(mid, require.module);
dojoRequireModuleStack.push(module);
injectModule(module);
});
checkDojoRequirePlugin();
},
touched,
traverse = function(m){
if(touched[m.mid] || /loadInit\!/.test(m.mid)){
// loadInit plugin modules are dependencies of modules in dojoRequireModuleStack...
// which would cause a circular dependency chain that would never be resolved if checked here
// notice all dependencies of any particular loadInit plugin module will already
// be checked since those are pushed into dojoRequireModuleStack explicitly by the
// plugin...so if a particular loadInitPlugin module's dependencies are not really
// on board, that *will* be detected elsewhere in the traversal.
return true;
}
touched[m.mid] = 1;
if(m.injected!==arrived && !m.executed){
return false;
}
for(var deps = m.deps || [], i= 0; i<deps.length; i++){
if(!traverse(deps[i])){
return false;
}
}
return true;
},
checkDojoRequirePlugin = function(){
touched = {};
dojoRequireModuleStack = array.filter(dojoRequireModuleStack, function(module){
return !traverse(module);
});
if(!dojoRequireModuleStack.length){
loaderVars.holdIdle();
var oldCallbacks = dojoRequireCallbacks;
dojoRequireCallbacks = [];
array.forEach(oldCallbacks, function(cb){cb(1);});
loaderVars.releaseIdle();
}
},
dojoLoadInitPlugin = function(mid, require, loaded){
// mid names a module that defines a "dojo load init" bundle, an object with two properties:
//
// * names: a vector of module ids that give top-level names to define in the lexical scope of def
// * def: a function that contains some some legacy loader API applications
//
// The point of def is to possibly cause some modules to be loaded (but not executed) by dojo/require! where the module
// ids are possibly-determined at runtime. For example, here is dojox.gfx from v1.6 expressed as an AMD module using the dojo/loadInit
// and dojo/require plugins.
//
// // dojox/gfx:
//
// define("*loadInit_12, {
// names:["dojo", "dijit", "dojox"],
// def: function(){
// dojo.loadInit(function(){
// var gfx = lang.getObject("dojox.gfx", true);
//
// //
// // code required to set gfx properties ommitted...
// //
//
// // now use the calculations to include the runtime-dependent module
// dojo.require("dojox.gfx." + gfx.renderer);
// });
// }
// });
//
// define(["dojo", "dojo/loadInit!" + id].concat("dojo/require!dojox/gfx/matric,dojox/gfx/_base"), function(dojo){
// // when this AMD factory function is executed, the following modules are guaranteed downloaded but not executed:
// // "dojox.gfx." + gfx.renderer
// // dojox.gfx.matrix
// // dojox.gfx._base
// dojo.provide("dojo.gfx");
// dojo.require("dojox.gfx.matrix");
// dojo.require("dojox.gfx._base");
// dojo.require("dojox.gfx." + gfx.renderer);
// return lang.getObject("dojo.gfx");
// });
// })();
//
// The idea is to run the legacy loader API with global variables shadowed, which allows these variables to
// be relocated. For example, dojox and dojo could be relocated to different names by giving a packageMap and the code above will
// execute properly (because the plugin below resolves the load init bundle.names module with respect to the module that demanded
// the plugin resource).
//
// Note that the relocation is specified in the runtime configuration; relocated names need not be set at build-time.
//
// Warning: this is not the best way to express dojox.gfx as and AMD module. In fact, the module has been properly converted in
// v1.7. However, this technique allows the builder to convert legacy modules into AMD modules and guarantee the codepath is the
// same in the converted AMD module.
require([mid], function(bundle){
// notice how names is resolved with respect to the module that demanded the plugin resource
require(bundle.names, function(){
// bring the bundle names into scope
for(var scopeText = "", args= [], i = 0; i<arguments.length; i++){
scopeText+= "var " + bundle.names[i] + "= arguments[" + i + "]; ";
args.push(arguments[i]);
}
eval(scopeText);
var callingModule = require.module,
deps = [],
hold = {},
requireList = [],
p,
syncLoaderApi = {
provide:function(moduleName){
// mark modules that arrive consequent to multiple provides in this module as arrived since they can't be injected
moduleName = slashName(moduleName);
var providedModule = getModule(moduleName, callingModule);
if(providedModule!==callingModule){
setArrived(providedModule);
}
},
require:function(moduleName, omitModuleCheck){
moduleName = slashName(moduleName);
omitModuleCheck && (getModule(moduleName, callingModule).result = nonmodule);
requireList.push(moduleName);
},
requireLocalization:function(moduleName, bundleName, locale){
// since we're going to need dojo/i8n, add it to deps if not already there
deps.length || (deps = ["dojo/i18n"]);
// figure out if the bundle is xdomain; if so, add it to the depsSet
locale = (locale || dojo.locale).toLowerCase();
moduleName = slashName(moduleName) + "/nls/" + (/root/i.test(locale) ? "" : locale + "/") + slashName(bundleName);
if(getModule(moduleName, callingModule).isXd){
deps.push("dojo/i18n!" + moduleName);
}// else the bundle will be loaded synchronously when the module is evaluated
},
loadInit:function(f){
f();
}
};
// hijack the correct dojo and apply bundle.def
try{
for(p in syncLoaderApi){
hold[p] = dojo[p];
dojo[p] = syncLoaderApi[p];
}
bundle.def.apply(null, args);
}catch(e){
signal("error", [makeErrorToken("failedDojoLoadInit"), e]);
}finally{
for(p in syncLoaderApi){
dojo[p] = hold[p];
}
}
// requireList is the list of modules that need to be downloaded but not executed before the callingModule can be executed
requireList.length && deps.push("dojo/require!" + requireList.join(","));
dojoRequireCallbacks.push(loaded);
array.forEach(requireList, function(mid){
var module = getModule(mid, require.module);
dojoRequireModuleStack.push(module);
injectModule(module);
});
checkDojoRequirePlugin();
});
});
},
extractApplication = function(
text, // the text to search
startSearch, // the position in text to start looking for the closing paren
startApplication // the position in text where the function application expression starts
){
// find end of the call by finding the matching end paren
// Warning: as usual, this will fail in the presense of unmatched right parans contained in strings, regexs, or unremoved comments
var parenRe = /\(|\)/g,
matchCount = 1,
match;
parenRe.lastIndex = startSearch;
while((match = parenRe.exec(text))){
if(match[0] == ")"){
matchCount -= 1;
}else{
matchCount += 1;
}
if(matchCount == 0){
break;
}
}
if(matchCount != 0){
throw "unmatched paren around character " + parenRe.lastIndex + " in: " + text;
}
//Put the master matching string in the results.
return [dojo.trim(text.substring(startApplication, parenRe.lastIndex))+";\n", parenRe.lastIndex];
},
// the following regex is taken from 1.6. It is a very poor technique to remove comments and
// will fail in some cases; for example, consider the code...
//
// var message = "Category-1 */* Category-2";
//
// The regex that follows will see a /* comment and trash the code accordingly. In fact, there are all
// kinds of cases like this with strings and regexs that will cause this design to fail miserably.
//
// Alternative regex designs exist that will result in less-likely failures, but will still fail in many cases.
// The only solution guaranteed 100% correct is to parse the code and that seems overkill for this
// backcompat/unbuilt-xdomain layer. In the end, since it's been this way for a while, we won't change it.
// See the opening paragraphs of Chapter 7 or ECME-262 which describes the lexical abiguity further.
removeCommentRe = /(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,
syncLoaderApiRe = /(^|\s)dojo\.(loadInit|require|provide|requireLocalization|requireIf|requireAfterIf|platformRequire)\s*\(/mg,
amdLoaderApiRe = /(^|\s)(require|define)\s*\(/m,
extractLegacyApiApplications = function(text, noCommentText){
// scan the noCommentText for any legacy loader API applications. Copy such applications into result (this is
// used by the builder). Move dojo.loadInit applications to loadInitApplications string. Copy all other applications
// to otherApplications string. If no applications were found, return 0, signalling an AMD module. Otherwise, return
// loadInitApplications + otherApplications. Fixup text by replacing
//
// dojo.loadInit(// etc...
//
// with
//
// \n 0 && dojo.loadInit(// etc...
//
// Which results in the dojo.loadInit from *not* being applied. This design goes a long way towards protecting the
// code from an over-agressive removeCommentRe. However...
//
// WARNING: the removeCommentRe will cause an error if a detected comment removes all or part of a legacy-loader application
// that is not in a comment.
var match, startSearch, startApplication, application,
loadInitApplications = [],
otherApplications = [],
allApplications = [];
// noCommentText may be provided by a build app with comments extracted by a better method than regex (hopefully)
noCommentText = noCommentText || text.replace(removeCommentRe, function(match){
// remove iff the detected comment has text that looks like a sync loader API application; this helps by
// removing as little as possible, minimizing the changes the janky regex will kill the module
syncLoaderApiRe.lastIndex = amdLoaderApiRe.lastIndex = 0;
return (syncLoaderApiRe.test(match) || amdLoaderApiRe.test(match)) ? "" : match;
});
// find and extract all dojo.loadInit applications
while((match = syncLoaderApiRe.exec(noCommentText))){
startSearch = syncLoaderApiRe.lastIndex;
startApplication = startSearch - match[0].length;
application = extractApplication(noCommentText, startSearch, startApplication);
if(match[2]=="loadInit"){
loadInitApplications.push(application[0]);
}else{
otherApplications.push(application[0]);
}
syncLoaderApiRe.lastIndex = application[1];
}
allApplications = loadInitApplications.concat(otherApplications);
if(allApplications.length || !amdLoaderApiRe.test(noCommentText)){
// either there were some legacy loader API applications or there were no AMD API applications
return [text.replace(/(^|\s)dojo\.loadInit\s*\(/g, "\n0 && dojo.loadInit("), allApplications.join(""), allApplications];
}else{
// legacy loader API *was not* detected and AMD API *was* detected; therefore, assume it's an AMD module
return 0;
}
},
transformToAmd = function(module, text){
// This is roughly the equivalent of dojo._xdCreateResource in 1.6-; however, it expresses a v1.6- dojo
// module in terms of AMD define instead of creating the dojo proprietary xdomain module expression.
// The module could have originated from several sources:
//
// * amd require() a module, e.g., require(["my/module"])
// * amd require() a nonmodule, e.g., require(["my/resource.js"')
// * amd define() deps vector (always a module)
// * dojo.require() a module, e.g. dojo.require("my.module")
// * dojo.require() a nonmodule, e.g., dojo.require("my.module", true)
// * dojo.requireIf/requireAfterIf/platformRequire a module
//
// The module is scanned for legacy loader API applications; if none are found, then assume the module is an
// AMD module and return 0. Otherwise, a synthetic dojo/loadInit plugin resource is created and the module text
// is rewritten as an AMD module with the single dependency of this synthetic resource. When the dojo/loadInit
// plugin loaded the synthetic resource, it will cause all dojo.loadInit's to be executed, find all dojo.require's
// (either directly consequent to dojo.require or indirectly consequent to dojo.require[After]If or
// dojo.platformRequire, and finally cause loading of all dojo.required modules with the dojo/require plugin. Thus,
// when the dojo/loadInit plugin reports it has been loaded, all modules required by the given module are guaranteed
// loaded (but not executed). This then allows the module to execute it's code path without interupts, thereby
// following the synchronous code path.
var extractResult, id, names = [], namesAsStrings = [];
if(buildDetectRe.test(text) || !(extractResult = extractLegacyApiApplications(text))){
// buildDetectRe.test(text) => a built module, always AMD
// extractResult==0 => no sync API
return 0;
}
// manufacture a synthetic module id that can never be a real mdule id (just like require does)
id = module.mid + "-*loadInit";
// construct the dojo/loadInit names vector which causes any relocated names to be defined as lexical variables under their not-relocated name
// the dojo/loadInit plugin assumes the first name in names is "dojo"
for(var p in getModule("dojo", module).result.scopeMap){
names.push(p);
namesAsStrings.push('"' + p + '"');
}
// rewrite the module as a synthetic dojo/loadInit plugin resource + the module expressed as an AMD module that depends on this synthetic resource
return "// xdomain rewrite of " + module.path + "\n" +
"define('" + id + "',{\n" +
"\tnames:" + dojo.toJson(names) + ",\n" +
"\tdef:function(" + names.join(",") + "){" + extractResult[1] + "}" +
"});\n\n" +
"define(" + dojo.toJson(names.concat(["dojo/loadInit!"+id])) + ", function(" + names.join(",") + "){\n" + extractResult[0] + "});";
},
loaderVars = require.initSyncLoader(dojoRequirePlugin, checkDojoRequirePlugin, transformToAmd),
sync =
loaderVars.sync,
xd =
loaderVars.xd,
arrived =
loaderVars.arrived,
nonmodule =
loaderVars.nonmodule,
executing =
loaderVars.executing,
executed =
loaderVars.executed,
syncExecStack =
loaderVars.syncExecStack,
modules =
loaderVars.modules,
execQ =
loaderVars.execQ,
getModule =
loaderVars.getModule,
injectModule =
loaderVars.injectModule,
setArrived =
loaderVars.setArrived,
signal =
loaderVars.signal,
finishExec =
loaderVars.finishExec,
execModule =
loaderVars.execModule,
getLegacyMode =
loaderVars.getLegacyMode;
dojo.provide = function(mid){
var executingModule = syncExecStack[0],
module = lang.mixin(getModule(slashName(mid), require.module), {
executed:executing,
result:lang.getObject(mid, true)
});
setArrived(module);
if(executingModule){
(executingModule.provides || (executingModule.provides = [])).push(function(){
module.result = lang.getObject(mid);
delete module.provides;
module.executed!==executed && finishExec(module);
});
}// else dojo.provide called not consequent to loading; therefore, give up trying to publish module value to loader namespace
return module.result;
};
has.add("config-publishRequireResult", 1, 0, 0);
dojo.require = function(moduleName, omitModuleCheck) {
// summary:
// loads a Javascript module from the appropriate URI
//
// moduleName: String
// module name to load, using periods for separators,
// e.g. "dojo.date.locale". Module paths are de-referenced by dojo's
// internal mapping of locations to names and are disambiguated by
// longest prefix. See `dojo.registerModulePath()` for details on
// registering new modules.
//
// omitModuleCheck: Boolean?
// if `true`, omitModuleCheck skips the step of ensuring that the
// loaded file actually defines the symbol it is referenced by.
// For example if it called as `dojo.require("a.b.c")` and the
// file located at `a/b/c.js` does not define an object `a.b.c`,
// and exception will be throws whereas no exception is raised
// when called as `dojo.require("a.b.c", true)`
//
// description:
// Modules are loaded via dojo.require by using one of two loaders: the normal loader
// and the xdomain loader. The xdomain loader is used when dojo was built with a
// custom build that specified loader=xdomain and the module lives on a modulePath
// that is a whole URL, with protocol and a domain. The versions of Dojo that are on
// the Google and AOL CDNs use the xdomain loader.
//
// If the module is loaded via the xdomain loader, it is an asynchronous load, since
// the module is added via a dynamically created script tag. This
// means that dojo.require() can return before the module has loaded. However, this
// should only happen in the case where you do dojo.require calls in the top-level
// HTML page, or if you purposely avoid the loader checking for dojo.require
// dependencies in your module by using a syntax like dojo["require"] to load the module.
//
// Sometimes it is useful to not have the loader detect the dojo.require calls in the
// module so that you can dynamically load the modules as a result of an action on the
// page, instead of right at module load time.
//
// Also, for script blocks in an HTML page, the loader does not pre-process them, so
// it does not know to download the modules before the dojo.require calls occur.
//
// So, in those two cases, when you want on-the-fly module loading or for script blocks
// in the HTML page, special care must be taken if the dojo.required code is loaded
// asynchronously. To make sure you can execute code that depends on the dojo.required
// modules, be sure to add the code that depends on the modules in a dojo.addOnLoad()
// callback. dojo.addOnLoad waits for all outstanding modules to finish loading before
// executing.
//
// This type of syntax works with both xdomain and normal loaders, so it is good
// practice to always use this idiom for on-the-fly code loading and in HTML script
// blocks. If at some point you change loaders and where the code is loaded from,
// it will all still work.
//
// More on how dojo.require
// `dojo.require("A.B")` first checks to see if symbol A.B is
// defined. If it is, it is simply returned (nothing to do).
//
// If it is not defined, it will look for `A/B.js` in the script root
// directory.
//
// `dojo.require` throws an exception if it cannot find a file
// to load, or if the symbol `A.B` is not defined after loading.
//
// It returns the object `A.B`, but note the caveats above about on-the-fly loading and
// HTML script blocks when the xdomain loader is loading a module.
//
// `dojo.require()` does nothing about importing symbols into
// the current namespace. It is presumed that the caller will
// take care of that.
//
// example:
// To use dojo.require in conjunction with dojo.ready:
//
// | dojo.require("foo");
// | dojo.require("bar");
// | dojo.addOnLoad(function(){
// | //you can now safely do something with foo and bar
// | });
//
// example:
// For example, to import all symbols into a local block, you might write:
//
// | with (dojo.require("A.B")) {
// | ...
// | }
//
// And to import just the leaf symbol to a local variable:
//
// | var B = dojo.require("A.B");
// | ...
//
// returns:
// the required namespace object
function doRequire(mid, omitModuleCheck){
var module = getModule(slashName(mid), require.module);
if(syncExecStack.length && syncExecStack[0].finish){
// switched to async loading in the middle of evaluating a legacy module; stop
// applying dojo.require so the remaining dojo.requires are applied in order
syncExecStack[0].finish.push(mid);
return undefined;
}
// recall module.executed has values {0, executing, executed}; therefore, truthy indicates executing or executed
if(module.executed){
return module.result;
}
omitModuleCheck && (module.result = nonmodule);
var currentMode = getLegacyMode();
// recall, in sync mode to inject is to *eval* the module text
// if the module is a legacy module, this is the same as executing
// but if the module is an AMD module, this means defining, not executing
injectModule(module);
// the inject may have changed the mode
currentMode = getLegacyMode();
// in sync mode to dojo.require is to execute
if(module.executed!==executed && module.injected===arrived){
// the module was already here before injectModule was called probably finishing up a xdomain
// load, but maybe a module given to the loader directly rather than having the loader retrieve it
loaderVars.holdIdle();
execModule(module);
loaderVars.releaseIdle();
}
if(module.executed){
return module.result;
}
if(currentMode==sync){
// the only way to get here is in sync mode and dojo.required a module that
// * was loaded async in the injectModule application a few lines up
// * was an AMD module that had deps that are being loaded async and therefore couldn't execute
if(module.cjs){
// the module was an AMD module; unshift, not push, which causes the current traversal to be reattempted from the top
execQ.unshift(module);
}else{
// the module was a legacy module
syncExecStack.length && (syncExecStack[0].finish= [mid]);
}
}else{
// the loader wasn't in sync mode on entry; probably async mode; therefore, no expectation of getting
// the module value synchronously; make sure it gets executed though
execQ.push(module);
}
return undefined;
}
var result = doRequire(moduleName, omitModuleCheck);
if(has("config-publishRequireResult") && !lang.exists(moduleName) && result!==undefined){
lang.setObject(moduleName, result);
}
return result;
};
dojo.loadInit = function(f) {
f();
};
dojo.registerModulePath = function(/*String*/moduleName, /*String*/prefix){
// summary:
// Maps a module name to a path
// description:
// An unregistered module is given the default path of ../[module],
// relative to Dojo root. For example, module acme is mapped to
// ../acme. If you want to use a different module name, use
// dojo.registerModulePath.
// example:
// If your dojo.js is located at this location in the web root:
// | /myapp/js/dojo/dojo/dojo.js
// and your modules are located at:
// | /myapp/js/foo/bar.js
// | /myapp/js/foo/baz.js
// | /myapp/js/foo/thud/xyzzy.js
// Your application can tell Dojo to locate the "foo" namespace by calling:
// | dojo.registerModulePath("foo", "../../foo");
// At which point you can then use dojo.require() to load the
// modules (assuming they provide() the same things which are
// required). The full code might be:
// | <script type="text/javascript"
// | src="/myapp/js/dojo/dojo/dojo.js"></script>
// | <script type="text/javascript">
// | dojo.registerModulePath("foo", "../../foo");
// | dojo.require("foo.bar");
// | dojo.require("foo.baz");
// | dojo.require("foo.thud.xyzzy");
// | </script>
var paths = {};
paths[moduleName.replace(/\./g, "/")] = prefix;
require({paths:paths});
};
dojo.platformRequire = function(/*Object*/modMap){
// summary:
// require one or more modules based on which host environment
// Dojo is currently operating in
// description:
// This method takes a "map" of arrays which one can use to
// optionally load dojo modules. The map is indexed by the
// possible dojo.name_ values, with two additional values:
// "default" and "common". The items in the "default" array will
// be loaded if none of the other items have been choosen based on
// dojo.name_, set by your host environment. The items in the
// "common" array will *always* be loaded, regardless of which
// list is chosen.
// example:
// | dojo.platformRequire({
// | browser: [
// | "foo.sample", // simple module
// | "foo.test",
// | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require)
// | ],
// | default: [ "foo.sample._base" ],
// | common: [ "important.module.common" ]
// | });
var result = (modMap.common || []).concat(modMap[dojo._name] || modMap["default"] || []),
temp;
while(result.length){
if(lang.isArray(temp = result.shift())){
dojo.require.apply(dojo, temp);
}else{
dojo.require(temp);
}
}
};
dojo.requireIf = dojo.requireAfterIf = function(/*Boolean*/ condition, /*String*/ moduleName, /*Boolean?*/omitModuleCheck){
// summary:
// If the condition is true then call `dojo.require()` for the specified
// resource
//
// example:
// | dojo.requireIf(dojo.isBrowser, "my.special.Module");
if(condition){
dojo.require(moduleName, omitModuleCheck);
}
};
dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
require(["../i18n"], function(i18n){
i18n.getLocalization(moduleName, bundleName, locale);
});
};
return {
extractLegacyApiApplications:extractLegacyApiApplications,
require:loaderVars.dojoRequirePlugin,
loadInit:dojoLoadInitPlugin
};
});