loader.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. define(["./kernel", "../has", "require", "module", "../json", "./lang", "./array"], function(dojo, has, require, thisModule, json, lang, array) {
  2. // module:
  3. // dojo/_base/loader
  4. // This module defines the v1.x synchronous loader API.
  5. // signal the loader in sync mode...
  6. //>>pure-amd
  7. if (!has("dojo-loader")){
  8. console.error("cannot load the Dojo v1.x loader with a foreign loader");
  9. return 0;
  10. }
  11. has.add("dojo-fast-sync-require", 1);
  12. var makeErrorToken = function(id){
  13. return {src:thisModule.id, id:id};
  14. },
  15. slashName = function(name){
  16. return name.replace(/\./g, "/");
  17. },
  18. buildDetectRe = /\/\/>>built/,
  19. dojoRequireCallbacks = [],
  20. dojoRequireModuleStack = [],
  21. dojoRequirePlugin = function(mid, require, loaded){
  22. dojoRequireCallbacks.push(loaded);
  23. array.forEach(mid.split(","), function(mid){
  24. var module = getModule(mid, require.module);
  25. dojoRequireModuleStack.push(module);
  26. injectModule(module);
  27. });
  28. checkDojoRequirePlugin();
  29. },
  30. checkDojoRequirePlugin = (has("dojo-fast-sync-require") ?
  31. // This version of checkDojoRequirePlugin makes the observation that all dojoRequireCallbacks can be released
  32. // when all *non-dojo/require!, dojo/loadInit!* modules are either executed, not requested, or arrived. This is
  33. // the case since there are no more modules the loader is waiting for, therefore, dojo/require! must have
  34. // everything it needs on board.
  35. //
  36. // The potential weakness of this algorithm is that dojo/require will not execute callbacks until *all* dependency
  37. // trees are ready. It is possible that some trees may be ready earlier than others, and this extra wait is non-optimal.
  38. // Still, for big projects, this seems better than the original algorithm below that proved slow in some cases.
  39. // Note, however, the original algorithm had the potential to execute partial trees, but that potential was never enabled.
  40. // There are also other optimization available with the original algorithm that have not been explored.
  41. function(){
  42. var module, mid;
  43. for(mid in modules){
  44. module = modules[mid];
  45. if(module.noReqPluginCheck===undefined){
  46. // tag the module as either a loadInit or require plugin or not for future reference
  47. module.noReqPluginCheck = /loadInit\!/.test(mid) || /require\!/.test(mid) ? 1 : 0;
  48. }
  49. if(!module.executed && !module.noReqPluginCheck && module.injected==requested){
  50. return;
  51. }
  52. }
  53. guardCheckComplete(function(){
  54. var oldCallbacks = dojoRequireCallbacks;
  55. dojoRequireCallbacks = [];
  56. array.forEach(oldCallbacks, function(cb){cb(1);});
  57. });
  58. } : (function(){
  59. // Note: this is the original checkDojoRequirePlugin that is much slower than the algorithm above. However, we know it
  60. // works, so we leave it here in case the algorithm above fails in some corner case.
  61. //
  62. // checkDojoRequirePlugin inspects all of the modules demanded by a dojo/require!<module-list> dependency
  63. // to see if they have arrived. The loader does not release *any* of these modules to be instantiated
  64. // until *all* of these modules are on board, thereby preventing the evaluation of a module with dojo.require's
  65. // that reference modules that are not available.
  66. //
  67. // The algorithm works by traversing the dependency graphs (remember, there can be cycles so they are not trees)
  68. // of each module in the dojoRequireModuleStack array (which contains the list of modules demanded by dojo/require!).
  69. // The moment a single module is discovered that is missing, the algorithm gives up and indicates that not all
  70. // modules are on board. dojo/loadInit! and dojo/require! are ignored because there dependencies are inserted
  71. // directly in dojoRequireModuleStack. For example, if "your/module" module depends on "dojo/require!my/module", then
  72. // *both* "dojo/require!my/module" and "my/module" will be in dojoRequireModuleStack. Obviously, if "my/module"
  73. // is on board, then "dojo/require!my/module" is also satisfied, so the algorithm doesn't check for "dojo/require!my/module".
  74. //
  75. // Note: inserting a dojo/require!<some-module-list> dependency in the dojoRequireModuleStack achieves nothing
  76. // with the current algorithm; however, having such modules present makes it possible to optimize the algorithm
  77. //
  78. // Note: prior versions of this algorithm had an optimization that signaled loaded on dojo/require! dependencies
  79. // individually (rather than waiting for them all to be resolved). The implementation proved problematic with cycles
  80. // and plugins. However, it is possible to reattach that strategy in the future.
  81. // a set from module-id to {undefined | 1 | 0}, where...
  82. // undefined => the module has not been inspected
  83. // 0 => the module or at least one of its dependencies has not arrived
  84. // 1 => the module is a loadInit! or require! plugin resource, or is currently being traversed (therefore, assume
  85. // OK until proven otherwise), or has been completely traversed and all dependencies have arrived
  86. var touched,
  87. traverse = function(m){
  88. touched[m.mid] = 1;
  89. for(var t, module, deps = m.deps || [], i= 0; i<deps.length; i++){
  90. module = deps[i];
  91. if(!(t = touched[module.mid])){
  92. if(t===0 || !traverse(module)){
  93. touched[m.mid] = 0;
  94. return false;
  95. }
  96. }
  97. }
  98. return true;
  99. };
  100. return function(){
  101. // initialize the touched hash with easy-to-compute values that help short circuit recursive algorithm;
  102. // recall loadInit/require plugin modules are dependencies of modules in dojoRequireModuleStack...
  103. // which would cause a circular dependency chain that would never be resolved if checked here
  104. // notice all dependencies of any particular loadInit/require plugin module will already
  105. // be checked since those are pushed into dojoRequireModuleStack explicitly by the
  106. // plugin...so if a particular loadInitPlugin module's dependencies are not really
  107. // on board, that *will* be detected elsewhere in the traversal.
  108. var module, mid;
  109. touched = {};
  110. for(mid in modules){
  111. module = modules[mid];
  112. if(module.executed || module.noReqPluginCheck){
  113. touched[mid] = 1;
  114. }else{
  115. if(module.noReqPluginCheck!==0){
  116. // tag the module as either a loadInit or require plugin or not for future reference
  117. module.noReqPluginCheck = /loadInit\!/.test(mid) || /require\!/.test(mid) ? 1 : 0;
  118. }
  119. if(module.noReqPluginCheck){
  120. touched[mid] = 1;
  121. }else if(module.injected!==arrived){
  122. // not executed, has not arrived, and is not a loadInit or require plugin resource
  123. touched[mid] = 0;
  124. }// else, leave undefined and we'll traverse the dependencies
  125. }
  126. }
  127. for(var t, i = 0, end = dojoRequireModuleStack.length; i<end; i++){
  128. module = dojoRequireModuleStack[i];
  129. if(!(t = touched[module.mid])){
  130. if(t===0 || !traverse(module)){
  131. return;
  132. }
  133. }
  134. }
  135. guardCheckComplete(function(){
  136. var oldCallbacks = dojoRequireCallbacks;
  137. dojoRequireCallbacks = [];
  138. array.forEach(oldCallbacks, function(cb){cb(1);});
  139. });
  140. };
  141. })()),
  142. dojoLoadInitPlugin = function(mid, require, loaded){
  143. // mid names a module that defines a "dojo load init" bundle, an object with two properties:
  144. //
  145. // * names: a vector of module ids that give top-level names to define in the lexical scope of def
  146. // * def: a function that contains some some legacy loader API applications
  147. //
  148. // The point of def is to possibly cause some modules to be loaded (but not executed) by dojo/require! where the module
  149. // ids are possibly-determined at runtime. For example, here is dojox.gfx from v1.6 expressed as an AMD module using the dojo/loadInit
  150. // and dojo/require plugins.
  151. //
  152. // // dojox/gfx:
  153. //
  154. // define("*loadInit_12, {
  155. // names:["dojo", "dijit", "dojox"],
  156. // def: function(){
  157. // dojo.loadInit(function(){
  158. // var gfx = lang.getObject("dojox.gfx", true);
  159. //
  160. // //
  161. // // code required to set gfx properties ommitted...
  162. // //
  163. //
  164. // // now use the calculations to include the runtime-dependent module
  165. // dojo.require("dojox.gfx." + gfx.renderer);
  166. // });
  167. // }
  168. // });
  169. //
  170. // define(["dojo", "dojo/loadInit!" + id].concat("dojo/require!dojox/gfx/matric,dojox/gfx/_base"), function(dojo){
  171. // // when this AMD factory function is executed, the following modules are guaranteed downloaded but not executed:
  172. // // "dojox.gfx." + gfx.renderer
  173. // // dojox.gfx.matrix
  174. // // dojox.gfx._base
  175. // dojo.provide("dojo.gfx");
  176. // dojo.require("dojox.gfx.matrix");
  177. // dojo.require("dojox.gfx._base");
  178. // dojo.require("dojox.gfx." + gfx.renderer);
  179. // return lang.getObject("dojo.gfx");
  180. // });
  181. // })();
  182. //
  183. // The idea is to run the legacy loader API with global variables shadowed, which allows these variables to
  184. // be relocated. For example, dojox and dojo could be relocated to different names by giving a map and the code above will
  185. // execute properly (because the plugin below resolves the load init bundle.names module with respect to the module that demanded
  186. // the plugin resource).
  187. //
  188. // Note that the relocation is specified in the runtime configuration; relocated names need not be set at build-time.
  189. //
  190. // Warning: this is not the best way to express dojox.gfx as and AMD module. In fact, the module has been properly converted in
  191. // v1.7. However, this technique allows the builder to convert legacy modules into AMD modules and guarantee the codepath is the
  192. // same in the converted AMD module.
  193. require([mid], function(bundle){
  194. // notice how names is resolved with respect to the module that demanded the plugin resource
  195. require(bundle.names, function(){
  196. // bring the bundle names into scope
  197. for(var scopeText = "", args= [], i = 0; i<arguments.length; i++){
  198. scopeText+= "var " + bundle.names[i] + "= arguments[" + i + "]; ";
  199. args.push(arguments[i]);
  200. }
  201. eval(scopeText);
  202. var callingModule = require.module,
  203. // the list of modules that need to be downloaded but not executed before the callingModule can be executed
  204. requireList = [],
  205. // the list of i18n bundles that are xdomain; undefined if none
  206. i18nDeps,
  207. syncLoaderApi = {
  208. provide:function(moduleName){
  209. // mark modules that arrive consequent to multiple provides in this module as arrived since they can't be injected
  210. moduleName = slashName(moduleName);
  211. var providedModule = getModule(moduleName, callingModule);
  212. if(providedModule!==callingModule){
  213. setArrived(providedModule);
  214. }
  215. },
  216. require:function(moduleName, omitModuleCheck){
  217. moduleName = slashName(moduleName);
  218. omitModuleCheck && (getModule(moduleName, callingModule).result = nonmodule);
  219. requireList.push(moduleName);
  220. },
  221. requireLocalization:function(moduleName, bundleName, locale){
  222. // since we're going to need dojo/i8n, add it to i18nDeps if not already there
  223. if(!i18nDeps){
  224. // don't have to map since that will occur when the dependency is resolved
  225. i18nDeps = ["dojo/i18n"];
  226. }
  227. // figure out if the bundle is xdomain; if so, add it to the i18nDepsSet
  228. locale = (locale || dojo.locale).toLowerCase();
  229. moduleName = slashName(moduleName) + "/nls/" + (/root/i.test(locale) ? "" : locale + "/") + slashName(bundleName);
  230. if(getModule(moduleName, callingModule).isXd){
  231. // don't have to map since that will occur when the dependency is resolved
  232. i18nDeps.push("dojo/i18n!" + moduleName);
  233. }// else the bundle will be loaded synchronously when the module is evaluated
  234. },
  235. loadInit:function(f){
  236. f();
  237. }
  238. },
  239. hold = {},
  240. p;
  241. // hijack the correct dojo and apply bundle.def
  242. try{
  243. for(p in syncLoaderApi){
  244. hold[p] = dojo[p];
  245. dojo[p] = syncLoaderApi[p];
  246. }
  247. bundle.def.apply(null, args);
  248. }catch(e){
  249. signal("error", [makeErrorToken("failedDojoLoadInit"), e]);
  250. }finally{
  251. for(p in syncLoaderApi){
  252. dojo[p] = hold[p];
  253. }
  254. }
  255. if(i18nDeps){
  256. requireList = requireList.concat(i18nDeps);
  257. }
  258. if(requireList.length){
  259. dojoRequirePlugin(requireList.join(","), require, loaded);
  260. }else{
  261. loaded();
  262. }
  263. });
  264. });
  265. },
  266. extractApplication = function(
  267. text, // the text to search
  268. startSearch, // the position in text to start looking for the closing paren
  269. startApplication // the position in text where the function application expression starts
  270. ){
  271. // find end of the call by finding the matching end paren
  272. // Warning: as usual, this will fail in the presence of unmatched right parans contained in strings, regexs, or unremoved comments
  273. var parenRe = /\(|\)/g,
  274. matchCount = 1,
  275. match;
  276. parenRe.lastIndex = startSearch;
  277. while((match = parenRe.exec(text))){
  278. if(match[0] == ")"){
  279. matchCount -= 1;
  280. }else{
  281. matchCount += 1;
  282. }
  283. if(matchCount == 0){
  284. break;
  285. }
  286. }
  287. if(matchCount != 0){
  288. throw "unmatched paren around character " + parenRe.lastIndex + " in: " + text;
  289. }
  290. //Put the master matching string in the results.
  291. return [dojo.trim(text.substring(startApplication, parenRe.lastIndex))+";\n", parenRe.lastIndex];
  292. },
  293. // the following regex is taken from 1.6. It is a very poor technique to remove comments and
  294. // will fail in some cases; for example, consider the code...
  295. //
  296. // var message = "Category-1 */* Category-2";
  297. //
  298. // The regex that follows will see a /* comment and trash the code accordingly. In fact, there are all
  299. // kinds of cases like this with strings and regexs that will cause this design to fail miserably.
  300. //
  301. // Alternative regex designs exist that will result in less-likely failures, but will still fail in many cases.
  302. // The only solution guaranteed 100% correct is to parse the code and that seems overkill for this
  303. // backcompat/unbuilt-xdomain layer. In the end, since it's been this way for a while, we won't change it.
  304. // See the opening paragraphs of Chapter 7 or ECME-262 which describes the lexical abiguity further.
  305. removeCommentRe = /(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,
  306. syncLoaderApiRe = /(^|\s)dojo\.(loadInit|require|provide|requireLocalization|requireIf|requireAfterIf|platformRequire)\s*\(/mg,
  307. amdLoaderApiRe = /(^|\s)(require|define)\s*\(/m,
  308. extractLegacyApiApplications = function(text, noCommentText){
  309. // scan the noCommentText for any legacy loader API applications. Copy such applications into result (this is
  310. // used by the builder). Move dojo.loadInit applications to loadInitApplications string. Copy all other applications
  311. // to otherApplications string. If no applications were found, return 0, signalling an AMD module. Otherwise, return
  312. // loadInitApplications + otherApplications. Fixup text by replacing
  313. //
  314. // dojo.loadInit(// etc...
  315. //
  316. // with
  317. //
  318. // \n 0 && dojo.loadInit(// etc...
  319. //
  320. // Which results in the dojo.loadInit from *not* being applied. This design goes a long way towards protecting the
  321. // code from an over-agressive removeCommentRe. However...
  322. //
  323. // WARNING: the removeCommentRe will cause an error if a detected comment removes all or part of a legacy-loader application
  324. // that is not in a comment.
  325. var match, startSearch, startApplication, application,
  326. loadInitApplications = [],
  327. otherApplications = [],
  328. allApplications = [];
  329. // noCommentText may be provided by a build app with comments extracted by a better method than regex (hopefully)
  330. noCommentText = noCommentText || text.replace(removeCommentRe, function(match){
  331. // remove iff the detected comment has text that looks like a sync loader API application; this helps by
  332. // removing as little as possible, minimizing the changes the janky regex will kill the module
  333. syncLoaderApiRe.lastIndex = amdLoaderApiRe.lastIndex = 0;
  334. return (syncLoaderApiRe.test(match) || amdLoaderApiRe.test(match)) ? "" : match;
  335. });
  336. // find and extract all dojo.loadInit applications
  337. while((match = syncLoaderApiRe.exec(noCommentText))){
  338. startSearch = syncLoaderApiRe.lastIndex;
  339. startApplication = startSearch - match[0].length;
  340. application = extractApplication(noCommentText, startSearch, startApplication);
  341. if(match[2]=="loadInit"){
  342. loadInitApplications.push(application[0]);
  343. }else{
  344. otherApplications.push(application[0]);
  345. }
  346. syncLoaderApiRe.lastIndex = application[1];
  347. }
  348. allApplications = loadInitApplications.concat(otherApplications);
  349. if(allApplications.length || !amdLoaderApiRe.test(noCommentText)){
  350. // either there were some legacy loader API applications or there were no AMD API applications
  351. return [text.replace(/(^|\s)dojo\.loadInit\s*\(/g, "\n0 && dojo.loadInit("), allApplications.join(""), allApplications];
  352. }else{
  353. // legacy loader API *was not* detected and AMD API *was* detected; therefore, assume it's an AMD module
  354. return 0;
  355. }
  356. },
  357. transformToAmd = function(module, text){
  358. // This is roughly the equivalent of dojo._xdCreateResource in 1.6-; however, it expresses a v1.6- dojo
  359. // module in terms of AMD define instead of creating the dojo proprietary xdomain module expression.
  360. // The module could have originated from several sources:
  361. //
  362. // * amd require() a module, e.g., require(["my/module"])
  363. // * amd require() a nonmodule, e.g., require(["my/resource.js"')
  364. // * amd define() deps vector (always a module)
  365. // * dojo.require() a module, e.g. dojo.require("my.module")
  366. // * dojo.require() a nonmodule, e.g., dojo.require("my.module", true)
  367. // * dojo.requireIf/requireAfterIf/platformRequire a module
  368. //
  369. // The module is scanned for legacy loader API applications; if none are found, then assume the module is an
  370. // AMD module and return 0. Otherwise, a synthetic dojo/loadInit plugin resource is created and the module text
  371. // is rewritten as an AMD module with the single dependency of this synthetic resource. When the dojo/loadInit
  372. // plugin loaded the synthetic resource, it will cause all dojo.loadInit's to be executed, find all dojo.require's
  373. // (either directly consequent to dojo.require or indirectly consequent to dojo.require[After]If or
  374. // dojo.platformRequire, and finally cause loading of all dojo.required modules with the dojo/require plugin. Thus,
  375. // when the dojo/loadInit plugin reports it has been loaded, all modules required by the given module are guaranteed
  376. // loaded (but not executed). This then allows the module to execute it's code path without interupts, thereby
  377. // following the synchronous code path.
  378. //
  379. // Notice that this function behaves the same whether or not it happens to be in a mapped dojo/loader module.
  380. var extractResult, id, names = [], namesAsStrings = [];
  381. if(buildDetectRe.test(text) || !(extractResult = extractLegacyApiApplications(text))){
  382. // buildDetectRe.test(text) => a built module, always AMD
  383. // extractResult==0 => no sync API
  384. return 0;
  385. }
  386. // manufacture a synthetic module id that can never be a real mdule id (just like require does)
  387. id = module.mid + "-*loadInit";
  388. // construct the dojo/loadInit names vector which causes any relocated names to be defined as lexical variables under their not-relocated name
  389. // the dojo/loadInit plugin assumes the first name in names is "dojo"
  390. for(var p in getModule("dojo", module).result.scopeMap){
  391. names.push(p);
  392. namesAsStrings.push('"' + p + '"');
  393. }
  394. // rewrite the module as a synthetic dojo/loadInit plugin resource + the module expressed as an AMD module that depends on this synthetic resource
  395. // don't have to map dojo/init since that will occur when the dependency is resolved
  396. return "// xdomain rewrite of " + module.mid + "\n" +
  397. "define('" + id + "',{\n" +
  398. "\tnames:" + json.stringify(names) + ",\n" +
  399. "\tdef:function(" + names.join(",") + "){" + extractResult[1] + "}" +
  400. "});\n\n" +
  401. "define(" + json.stringify(names.concat(["dojo/loadInit!"+id])) + ", function(" + names.join(",") + "){\n" + extractResult[0] + "});";
  402. },
  403. loaderVars = require.initSyncLoader(dojoRequirePlugin, checkDojoRequirePlugin, transformToAmd),
  404. sync =
  405. loaderVars.sync,
  406. requested =
  407. loaderVars.requested,
  408. arrived =
  409. loaderVars.arrived,
  410. nonmodule =
  411. loaderVars.nonmodule,
  412. executing =
  413. loaderVars.executing,
  414. executed =
  415. loaderVars.executed,
  416. syncExecStack =
  417. loaderVars.syncExecStack,
  418. modules =
  419. loaderVars.modules,
  420. execQ =
  421. loaderVars.execQ,
  422. getModule =
  423. loaderVars.getModule,
  424. injectModule =
  425. loaderVars.injectModule,
  426. setArrived =
  427. loaderVars.setArrived,
  428. signal =
  429. loaderVars.signal,
  430. finishExec =
  431. loaderVars.finishExec,
  432. execModule =
  433. loaderVars.execModule,
  434. getLegacyMode =
  435. loaderVars.getLegacyMode,
  436. guardCheckComplete =
  437. loaderVars.guardCheckComplete;
  438. // there is exactly one dojoRequirePlugin among possibly-many dojo/_base/loader's (owing to mapping)
  439. dojoRequirePlugin = loaderVars.dojoRequirePlugin;
  440. dojo.provide = function(mid){
  441. var executingModule = syncExecStack[0],
  442. module = lang.mixin(getModule(slashName(mid), require.module), {
  443. executed:executing,
  444. result:lang.getObject(mid, true)
  445. });
  446. setArrived(module);
  447. if(executingModule){
  448. (executingModule.provides || (executingModule.provides = [])).push(function(){
  449. module.result = lang.getObject(mid);
  450. delete module.provides;
  451. module.executed!==executed && finishExec(module);
  452. });
  453. }// else dojo.provide called not consequent to loading; therefore, give up trying to publish module value to loader namespace
  454. return module.result;
  455. };
  456. has.add("config-publishRequireResult", 1, 0, 0);
  457. dojo.require = function(moduleName, omitModuleCheck) {
  458. // summary:
  459. // loads a Javascript module from the appropriate URI
  460. //
  461. // moduleName: String
  462. // module name to load, using periods for separators,
  463. // e.g. "dojo.date.locale". Module paths are de-referenced by dojo's
  464. // internal mapping of locations to names and are disambiguated by
  465. // longest prefix. See `dojo.registerModulePath()` for details on
  466. // registering new modules.
  467. //
  468. // omitModuleCheck: Boolean?
  469. // if `true`, omitModuleCheck skips the step of ensuring that the
  470. // loaded file actually defines the symbol it is referenced by.
  471. // For example if it called as `dojo.require("a.b.c")` and the
  472. // file located at `a/b/c.js` does not define an object `a.b.c`,
  473. // and exception will be throws whereas no exception is raised
  474. // when called as `dojo.require("a.b.c", true)`
  475. //
  476. // description:
  477. // Modules are loaded via dojo.require by using one of two loaders: the normal loader
  478. // and the xdomain loader. The xdomain loader is used when dojo was built with a
  479. // custom build that specified loader=xdomain and the module lives on a modulePath
  480. // that is a whole URL, with protocol and a domain. The versions of Dojo that are on
  481. // the Google and AOL CDNs use the xdomain loader.
  482. //
  483. // If the module is loaded via the xdomain loader, it is an asynchronous load, since
  484. // the module is added via a dynamically created script tag. This
  485. // means that dojo.require() can return before the module has loaded. However, this
  486. // should only happen in the case where you do dojo.require calls in the top-level
  487. // HTML page, or if you purposely avoid the loader checking for dojo.require
  488. // dependencies in your module by using a syntax like dojo["require"] to load the module.
  489. //
  490. // Sometimes it is useful to not have the loader detect the dojo.require calls in the
  491. // module so that you can dynamically load the modules as a result of an action on the
  492. // page, instead of right at module load time.
  493. //
  494. // Also, for script blocks in an HTML page, the loader does not pre-process them, so
  495. // it does not know to download the modules before the dojo.require calls occur.
  496. //
  497. // So, in those two cases, when you want on-the-fly module loading or for script blocks
  498. // in the HTML page, special care must be taken if the dojo.required code is loaded
  499. // asynchronously. To make sure you can execute code that depends on the dojo.required
  500. // modules, be sure to add the code that depends on the modules in a dojo.addOnLoad()
  501. // callback. dojo.addOnLoad waits for all outstanding modules to finish loading before
  502. // executing.
  503. //
  504. // This type of syntax works with both xdomain and normal loaders, so it is good
  505. // practice to always use this idiom for on-the-fly code loading and in HTML script
  506. // blocks. If at some point you change loaders and where the code is loaded from,
  507. // it will all still work.
  508. //
  509. // More on how dojo.require
  510. // `dojo.require("A.B")` first checks to see if symbol A.B is
  511. // defined. If it is, it is simply returned (nothing to do).
  512. //
  513. // If it is not defined, it will look for `A/B.js` in the script root
  514. // directory.
  515. //
  516. // `dojo.require` throws an exception if it cannot find a file
  517. // to load, or if the symbol `A.B` is not defined after loading.
  518. //
  519. // It returns the object `A.B`, but note the caveats above about on-the-fly loading and
  520. // HTML script blocks when the xdomain loader is loading a module.
  521. //
  522. // `dojo.require()` does nothing about importing symbols into
  523. // the current namespace. It is presumed that the caller will
  524. // take care of that.
  525. //
  526. // example:
  527. // To use dojo.require in conjunction with dojo.ready:
  528. //
  529. // | dojo.require("foo");
  530. // | dojo.require("bar");
  531. // | dojo.addOnLoad(function(){
  532. // | //you can now safely do something with foo and bar
  533. // | });
  534. //
  535. // example:
  536. // For example, to import all symbols into a local block, you might write:
  537. //
  538. // | with (dojo.require("A.B")) {
  539. // | ...
  540. // | }
  541. //
  542. // And to import just the leaf symbol to a local variable:
  543. //
  544. // | var B = dojo.require("A.B");
  545. // | ...
  546. //
  547. // returns:
  548. // the required namespace object
  549. function doRequire(mid, omitModuleCheck){
  550. var module = getModule(slashName(mid), require.module);
  551. if(syncExecStack.length && syncExecStack[0].finish){
  552. // switched to async loading in the middle of evaluating a legacy module; stop
  553. // applying dojo.require so the remaining dojo.requires are applied in order
  554. syncExecStack[0].finish.push(mid);
  555. return undefined;
  556. }
  557. // recall module.executed has values {0, executing, executed}; therefore, truthy indicates executing or executed
  558. if(module.executed){
  559. return module.result;
  560. }
  561. omitModuleCheck && (module.result = nonmodule);
  562. // rcg...why here and in two lines??
  563. var currentMode = getLegacyMode();
  564. // recall, in sync mode to inject is to *eval* the module text
  565. // if the module is a legacy module, this is the same as executing
  566. // but if the module is an AMD module, this means defining, not executing
  567. injectModule(module);
  568. // the inject may have changed the mode
  569. currentMode = getLegacyMode();
  570. // in sync mode to dojo.require is to execute
  571. if(module.executed!==executed && module.injected===arrived){
  572. // the module was already here before injectModule was called probably finishing up a xdomain
  573. // load, but maybe a module given to the loader directly rather than having the loader retrieve it
  574. loaderVars.guardCheckComplete(function(){
  575. execModule(module);
  576. });
  577. }
  578. if(module.executed){
  579. return module.result;
  580. }
  581. if(currentMode==sync){
  582. // the only way to get here is in sync mode and dojo.required a module that
  583. // * was loaded async in the injectModule application a few lines up
  584. // * was an AMD module that had deps that are being loaded async and therefore couldn't execute
  585. if(module.cjs){
  586. // the module was an AMD module; unshift, not push, which causes the current traversal to be reattempted from the top
  587. execQ.unshift(module);
  588. }else{
  589. // the module was a legacy module
  590. syncExecStack.length && (syncExecStack[0].finish= [mid]);
  591. }
  592. }else{
  593. // the loader wasn't in sync mode on entry; probably async mode; therefore, no expectation of getting
  594. // the module value synchronously; make sure it gets executed though
  595. execQ.push(module);
  596. }
  597. return undefined;
  598. }
  599. var result = doRequire(moduleName, omitModuleCheck);
  600. if(has("config-publishRequireResult") && !lang.exists(moduleName) && result!==undefined){
  601. lang.setObject(moduleName, result);
  602. }
  603. return result;
  604. };
  605. dojo.loadInit = function(f) {
  606. f();
  607. };
  608. dojo.registerModulePath = function(/*String*/moduleName, /*String*/prefix){
  609. // summary:
  610. // Maps a module name to a path
  611. // description:
  612. // An unregistered module is given the default path of ../[module],
  613. // relative to Dojo root. For example, module acme is mapped to
  614. // ../acme. If you want to use a different module name, use
  615. // dojo.registerModulePath.
  616. // example:
  617. // If your dojo.js is located at this location in the web root:
  618. // | /myapp/js/dojo/dojo/dojo.js
  619. // and your modules are located at:
  620. // | /myapp/js/foo/bar.js
  621. // | /myapp/js/foo/baz.js
  622. // | /myapp/js/foo/thud/xyzzy.js
  623. // Your application can tell Dojo to locate the "foo" namespace by calling:
  624. // | dojo.registerModulePath("foo", "../../foo");
  625. // At which point you can then use dojo.require() to load the
  626. // modules (assuming they provide() the same things which are
  627. // required). The full code might be:
  628. // | <script type="text/javascript"
  629. // | src="/myapp/js/dojo/dojo/dojo.js"></script>
  630. // | <script type="text/javascript">
  631. // | dojo.registerModulePath("foo", "../../foo");
  632. // | dojo.require("foo.bar");
  633. // | dojo.require("foo.baz");
  634. // | dojo.require("foo.thud.xyzzy");
  635. // | </script>
  636. var paths = {};
  637. paths[moduleName.replace(/\./g, "/")] = prefix;
  638. require({paths:paths});
  639. };
  640. dojo.platformRequire = function(/*Object*/modMap){
  641. // summary:
  642. // require one or more modules based on which host environment
  643. // Dojo is currently operating in
  644. // description:
  645. // This method takes a "map" of arrays which one can use to
  646. // optionally load dojo modules. The map is indexed by the
  647. // possible dojo.name_ values, with two additional values:
  648. // "default" and "common". The items in the "default" array will
  649. // be loaded if none of the other items have been chosen based on
  650. // dojo.name_, set by your host environment. The items in the
  651. // "common" array will *always* be loaded, regardless of which
  652. // list is chosen.
  653. // example:
  654. // | dojo.platformRequire({
  655. // | browser: [
  656. // | "foo.sample", // simple module
  657. // | "foo.test",
  658. // | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require)
  659. // | ],
  660. // | default: [ "foo.sample._base" ],
  661. // | common: [ "important.module.common" ]
  662. // | });
  663. var result = (modMap.common || []).concat(modMap[dojo._name] || modMap["default"] || []),
  664. temp;
  665. while(result.length){
  666. if(lang.isArray(temp = result.shift())){
  667. dojo.require.apply(dojo, temp);
  668. }else{
  669. dojo.require(temp);
  670. }
  671. }
  672. };
  673. dojo.requireIf = dojo.requireAfterIf = function(/*Boolean*/ condition, /*String*/ moduleName, /*Boolean?*/omitModuleCheck){
  674. // summary:
  675. // If the condition is true then call `dojo.require()` for the specified
  676. // resource
  677. //
  678. // example:
  679. // | dojo.requireIf(dojo.isBrowser, "my.special.Module");
  680. if(condition){
  681. dojo.require(moduleName, omitModuleCheck);
  682. }
  683. };
  684. dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
  685. require(["../i18n"], function(i18n){
  686. i18n.getLocalization(moduleName, bundleName, locale);
  687. });
  688. };
  689. return {
  690. // summary:
  691. // This module defines the v1.x synchronous loader API.
  692. extractLegacyApiApplications:extractLegacyApiApplications,
  693. require:dojoRequirePlugin,
  694. loadInit:dojoLoadInitPlugin
  695. };
  696. });