1 | (function( |
---|
2 | userConfig, |
---|
3 | defaultConfig |
---|
4 | ){ |
---|
5 | // summary: |
---|
6 | // This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with |
---|
7 | // any AMD-compliant loader via the package main module dojo/main. |
---|
8 | // description: |
---|
9 | // This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured |
---|
10 | // to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded |
---|
11 | // IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package |
---|
12 | // via the package main module dojo/main and this loader is not required; see dojo/package.json for details. |
---|
13 | // |
---|
14 | // In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables |
---|
15 | // the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed |
---|
16 | // via the has.js API and statically removed via the build system. |
---|
17 | // |
---|
18 | // This loader includes sniffing machinery to determine the environment; the following environments are supported: |
---|
19 | // |
---|
20 | // - browser |
---|
21 | // - node.js |
---|
22 | // - rhino |
---|
23 | // |
---|
24 | // This is the so-called "source loader". As such, it includes many optional features that may be discarded by |
---|
25 | // building a customized version with the build system. |
---|
26 | |
---|
27 | // Design and Implementation Notes |
---|
28 | // |
---|
29 | // This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC. |
---|
30 | // |
---|
31 | // This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) |
---|
32 | // loader that can be configured to operate in either synchronous or asynchronous modes. |
---|
33 | // |
---|
34 | // Since this machinery implements a loader, it does not have the luxury of using a load system and/or |
---|
35 | // leveraging a utility library. This results in an unpleasantly long file; here is a road map of the contents: |
---|
36 | // |
---|
37 | // 1. Small library for use implementing the loader. |
---|
38 | // 2. Define the has.js API; this is used throughout the loader to bracket features. |
---|
39 | // 3. Define the node.js and rhino sniffs and sniff. |
---|
40 | // 4. Define the loader's data. |
---|
41 | // 5. Define the configuration machinery. |
---|
42 | // 6. Define the script element sniffing machinery and sniff for configuration data. |
---|
43 | // 7. Configure the loader IAW the provided user, default, and sniffing data. |
---|
44 | // 8. Define the global require function. |
---|
45 | // 9. Define the module resolution machinery. |
---|
46 | // 10. Define the module and plugin module definition machinery |
---|
47 | // 11. Define the script injection machinery. |
---|
48 | // 12. Define the window load detection. |
---|
49 | // 13. Define the logging API. |
---|
50 | // 14. Define the tracing API. |
---|
51 | // 16. Define the AMD define function. |
---|
52 | // 17. Define the dojo v1.x provide/require machinery--so called "legacy" modes. |
---|
53 | // 18. Publish global variables. |
---|
54 | // |
---|
55 | // Language and Acronyms and Idioms |
---|
56 | // |
---|
57 | // moduleId: a CJS module identifier, (used for public APIs) |
---|
58 | // mid: moduleId (used internally) |
---|
59 | // packageId: a package identifier (used for public APIs) |
---|
60 | // pid: packageId (used internally); the implied system or default package has pid==="" |
---|
61 | // pack: package is used internally to reference a package object (since javascript has reserved words including "package") |
---|
62 | // prid: plugin resource identifier |
---|
63 | // The integer constant 1 is used in place of true and 0 in place of false. |
---|
64 | |
---|
65 | // define a minimal library to help build the loader |
---|
66 | var noop = function(){ |
---|
67 | }, |
---|
68 | |
---|
69 | isEmpty = function(it){ |
---|
70 | for(var p in it){ |
---|
71 | return 0; |
---|
72 | } |
---|
73 | return 1; |
---|
74 | }, |
---|
75 | |
---|
76 | toString = {}.toString, |
---|
77 | |
---|
78 | isFunction = function(it){ |
---|
79 | return toString.call(it) == "[object Function]"; |
---|
80 | }, |
---|
81 | |
---|
82 | isString = function(it){ |
---|
83 | return toString.call(it) == "[object String]"; |
---|
84 | }, |
---|
85 | |
---|
86 | isArray = function(it){ |
---|
87 | return toString.call(it) == "[object Array]"; |
---|
88 | }, |
---|
89 | |
---|
90 | forEach = function(vector, callback){ |
---|
91 | if(vector){ |
---|
92 | for(var i = 0; i < vector.length;){ |
---|
93 | callback(vector[i++]); |
---|
94 | } |
---|
95 | } |
---|
96 | }, |
---|
97 | |
---|
98 | mix = function(dest, src){ |
---|
99 | for(var p in src){ |
---|
100 | dest[p] = src[p]; |
---|
101 | } |
---|
102 | return dest; |
---|
103 | }, |
---|
104 | |
---|
105 | makeError = function(error, info){ |
---|
106 | return mix(new Error(error), {src:"dojoLoader", info:info}); |
---|
107 | }, |
---|
108 | |
---|
109 | uidSeed = 1, |
---|
110 | |
---|
111 | uid = function(){ |
---|
112 | // Returns a unique identifier (within the lifetime of the document) of the form /_d+/. |
---|
113 | return "_" + uidSeed++; |
---|
114 | }, |
---|
115 | |
---|
116 | // FIXME: how to doc window.require() api |
---|
117 | |
---|
118 | // this will be the global require function; define it immediately so we can start hanging things off of it |
---|
119 | req = function( |
---|
120 | config, //(object, optional) hash of configuration properties |
---|
121 | dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback |
---|
122 | callback //(function, optional) lambda expression to apply to module values implied by dependencies |
---|
123 | ){ |
---|
124 | return contextRequire(config, dependencies, callback, 0, req); |
---|
125 | }, |
---|
126 | |
---|
127 | // the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout |
---|
128 | global = this, |
---|
129 | |
---|
130 | doc = global.document, |
---|
131 | |
---|
132 | element = doc && doc.createElement("DiV"), |
---|
133 | |
---|
134 | has = req.has = function(name){ |
---|
135 | return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name]; |
---|
136 | }, |
---|
137 | |
---|
138 | hasCache = has.cache = defaultConfig.hasCache; |
---|
139 | |
---|
140 | has.add = function(name, test, now, force){ |
---|
141 | (hasCache[name]===undefined || force) && (hasCache[name] = test); |
---|
142 | return now && has(name); |
---|
143 | }; |
---|
144 | |
---|
145 | has.add("host-node", userConfig.has && "host-node" in userConfig.has ? |
---|
146 | userConfig.has["host-node"] : |
---|
147 | (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); |
---|
148 | if(has("host-node")){ |
---|
149 | // fixup the default config for node.js environment |
---|
150 | require("./_base/configNode.js").config(defaultConfig); |
---|
151 | // remember node's require (with respect to baseUrl==dojo's root) |
---|
152 | defaultConfig.loaderPatch.nodeRequire = require; |
---|
153 | } |
---|
154 | |
---|
155 | has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ? |
---|
156 | userConfig.has["host-rhino"] : |
---|
157 | (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); |
---|
158 | if(has("host-rhino")){ |
---|
159 | // owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl... |
---|
160 | for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){ |
---|
161 | arg = (rhinoArgs[i++] + "").split("="); |
---|
162 | if(arg[0] == "baseUrl"){ |
---|
163 | baseUrl = arg[1]; |
---|
164 | break; |
---|
165 | } |
---|
166 | } |
---|
167 | load(baseUrl + "/_base/configRhino.js"); |
---|
168 | rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs); |
---|
169 | } |
---|
170 | |
---|
171 | // userConfig has tests override defaultConfig has tests; do this after the environment detection because |
---|
172 | // the environment detection usually sets some has feature values in the hasCache. |
---|
173 | for(var p in userConfig.has){ |
---|
174 | has.add(p, userConfig.has[p], 0, 1); |
---|
175 | } |
---|
176 | |
---|
177 | // |
---|
178 | // define the loader data |
---|
179 | // |
---|
180 | |
---|
181 | // the loader will use these like symbols if the loader has the traceApi; otherwise |
---|
182 | // define magic numbers so that modules can be provided as part of defaultConfig |
---|
183 | var requested = 1, |
---|
184 | arrived = 2, |
---|
185 | nonmodule = 3, |
---|
186 | executing = 4, |
---|
187 | executed = 5; |
---|
188 | |
---|
189 | if(has("dojo-trace-api")){ |
---|
190 | // these make debugging nice; but using strings for symbols is a gross rookie error; don't do it for production code |
---|
191 | requested = "requested"; |
---|
192 | arrived = "arrived"; |
---|
193 | nonmodule = "not-a-module"; |
---|
194 | executing = "executing"; |
---|
195 | executed = "executed"; |
---|
196 | } |
---|
197 | |
---|
198 | var legacyMode = 0, |
---|
199 | sync = "sync", |
---|
200 | xd = "xd", |
---|
201 | syncExecStack = [], |
---|
202 | dojoRequirePlugin = 0, |
---|
203 | checkDojoRequirePlugin = noop, |
---|
204 | transformToAmd = noop, |
---|
205 | getXhr; |
---|
206 | if(has("dojo-sync-loader")){ |
---|
207 | req.isXdUrl = noop; |
---|
208 | |
---|
209 | req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){ |
---|
210 | // the first dojo/_base/loader loaded gets to define these variables; they are designed to work |
---|
211 | // in the presence of zero to many mapped dojo/_base/loaders |
---|
212 | if(!dojoRequirePlugin){ |
---|
213 | dojoRequirePlugin = dojoRequirePlugin_; |
---|
214 | checkDojoRequirePlugin = checkDojoRequirePlugin_; |
---|
215 | transformToAmd = transformToAmd_; |
---|
216 | } |
---|
217 | |
---|
218 | return { |
---|
219 | sync:sync, |
---|
220 | requested:requested, |
---|
221 | arrived:arrived, |
---|
222 | nonmodule:nonmodule, |
---|
223 | executing:executing, |
---|
224 | executed:executed, |
---|
225 | syncExecStack:syncExecStack, |
---|
226 | modules:modules, |
---|
227 | execQ:execQ, |
---|
228 | getModule:getModule, |
---|
229 | injectModule:injectModule, |
---|
230 | setArrived:setArrived, |
---|
231 | signal:signal, |
---|
232 | finishExec:finishExec, |
---|
233 | execModule:execModule, |
---|
234 | dojoRequirePlugin:dojoRequirePlugin, |
---|
235 | getLegacyMode:function(){return legacyMode;}, |
---|
236 | guardCheckComplete:guardCheckComplete |
---|
237 | }; |
---|
238 | }; |
---|
239 | |
---|
240 | if(has("dom")){ |
---|
241 | // in legacy sync mode, the loader needs a minimal XHR library |
---|
242 | |
---|
243 | var locationProtocol = location.protocol, |
---|
244 | locationHost = location.host; |
---|
245 | req.isXdUrl = function(url){ |
---|
246 | if(/^\./.test(url)){ |
---|
247 | // begins with a dot is always relative to page URL; therefore not xdomain |
---|
248 | return false; |
---|
249 | } |
---|
250 | if(/^\/\//.test(url)){ |
---|
251 | // for v1.6- backcompat, url starting with // indicates xdomain |
---|
252 | return true; |
---|
253 | } |
---|
254 | // get protocol and host |
---|
255 | // \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file |
---|
256 | // locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false |
---|
257 | var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/); |
---|
258 | return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost)); |
---|
259 | }; |
---|
260 | |
---|
261 | |
---|
262 | // note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config |
---|
263 | has.add("dojo-xhr-factory", 1); |
---|
264 | has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:"); |
---|
265 | has.add("native-xhr", typeof XMLHttpRequest != "undefined"); |
---|
266 | if(has("native-xhr") && !has("dojo-force-activex-xhr")){ |
---|
267 | getXhr = function(){ |
---|
268 | return new XMLHttpRequest(); |
---|
269 | }; |
---|
270 | }else{ |
---|
271 | // if in the browser an old IE; find an xhr |
---|
272 | for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){ |
---|
273 | try{ |
---|
274 | progid = XMLHTTP_PROGIDS[i++]; |
---|
275 | if(new ActiveXObject(progid)){ |
---|
276 | // this progid works; therefore, use it from now on |
---|
277 | break; |
---|
278 | } |
---|
279 | }catch(e){ |
---|
280 | // squelch; we're just trying to find a good ActiveX progid |
---|
281 | // if they all fail, then progid ends up as the last attempt and that will signal the error |
---|
282 | // the first time the client actually tries to exec an xhr |
---|
283 | } |
---|
284 | } |
---|
285 | getXhr = function(){ |
---|
286 | return new ActiveXObject(progid); |
---|
287 | }; |
---|
288 | } |
---|
289 | req.getXhr = getXhr; |
---|
290 | |
---|
291 | has.add("dojo-gettext-api", 1); |
---|
292 | req.getText = function(url, async, onLoad){ |
---|
293 | var xhr = getXhr(); |
---|
294 | xhr.open('GET', fixupUrl(url), false); |
---|
295 | xhr.send(null); |
---|
296 | if(xhr.status == 200 || (!location.host && !xhr.status)){ |
---|
297 | if(onLoad){ |
---|
298 | onLoad(xhr.responseText, async); |
---|
299 | } |
---|
300 | }else{ |
---|
301 | throw makeError("xhrFailed", xhr.status); |
---|
302 | } |
---|
303 | return xhr.responseText; |
---|
304 | }; |
---|
305 | } |
---|
306 | }else{ |
---|
307 | req.async = 1; |
---|
308 | } |
---|
309 | |
---|
310 | // |
---|
311 | // loader eval |
---|
312 | // |
---|
313 | var eval_ = |
---|
314 | // use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution |
---|
315 | new Function('return eval(arguments[0]);'); |
---|
316 | |
---|
317 | req.eval = |
---|
318 | function(text, hint){ |
---|
319 | return eval_(text + "\r\n////@ sourceURL=" + hint); |
---|
320 | }; |
---|
321 | |
---|
322 | // |
---|
323 | // loader micro events API |
---|
324 | // |
---|
325 | var listenerQueues = {}, |
---|
326 | error = "error", |
---|
327 | signal = req.signal = function(type, args){ |
---|
328 | var queue = listenerQueues[type]; |
---|
329 | // notice we run a copy of the queue; this allows listeners to add/remove |
---|
330 | // other listeners without affecting this particular signal |
---|
331 | forEach(queue && queue.slice(0), function(listener){ |
---|
332 | listener.apply(null, isArray(args) ? args : [args]); |
---|
333 | }); |
---|
334 | }, |
---|
335 | on = req.on = function(type, listener){ |
---|
336 | // notice a queue is not created until a client actually connects |
---|
337 | var queue = listenerQueues[type] || (listenerQueues[type] = []); |
---|
338 | queue.push(listener); |
---|
339 | return { |
---|
340 | remove:function(){ |
---|
341 | for(var i = 0; i<queue.length; i++){ |
---|
342 | if(queue[i]===listener){ |
---|
343 | queue.splice(i, 1); |
---|
344 | return; |
---|
345 | } |
---|
346 | } |
---|
347 | } |
---|
348 | }; |
---|
349 | }; |
---|
350 | |
---|
351 | // configuration machinery; with an optimized/built defaultConfig, all configuration machinery can be discarded |
---|
352 | // lexical variables hold key loader data structures to help with minification; these may be completely, |
---|
353 | // one-time initialized by defaultConfig for optimized/built versions |
---|
354 | var |
---|
355 | aliases |
---|
356 | // a vector of pairs of [regexs or string, replacement] => (alias, actual) |
---|
357 | = [], |
---|
358 | |
---|
359 | paths |
---|
360 | // CommonJS paths |
---|
361 | = {}, |
---|
362 | |
---|
363 | pathsMapProg |
---|
364 | // list of (from-path, to-path, regex, length) derived from paths; |
---|
365 | // a "program" to apply paths; see computeMapProg |
---|
366 | = [], |
---|
367 | |
---|
368 | packs |
---|
369 | // a map from packageId to package configuration object; see fixupPackageInfo |
---|
370 | = {}, |
---|
371 | |
---|
372 | map = req.map |
---|
373 | // AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map |
---|
374 | = {}, |
---|
375 | |
---|
376 | mapProgs |
---|
377 | // vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value |
---|
378 | = [], |
---|
379 | |
---|
380 | modules |
---|
381 | // A hash:(mid) --> (module-object) the module namespace |
---|
382 | // |
---|
383 | // pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package |
---|
384 | // mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script") |
---|
385 | // url: the URL from which the module was retrieved |
---|
386 | // pack: the package object of the package to which the module belongs |
---|
387 | // executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed |
---|
388 | // deps: the dependency vector for this module (vector of modules objects) |
---|
389 | // def: the factory for this module |
---|
390 | // result: the result of the running the factory for this module |
---|
391 | // injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define |
---|
392 | // load: plugin load function; applicable only for plugins |
---|
393 | // |
---|
394 | // Modules go through several phases in creation: |
---|
395 | // |
---|
396 | // 1. Requested: some other module's definition or a require application contained the requested module in |
---|
397 | // its dependency vector or executing code explicitly demands a module via req.require. |
---|
398 | // |
---|
399 | // 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL |
---|
400 | // |
---|
401 | // 3. Loaded: the resource injected in [2] has been evaluated. |
---|
402 | // |
---|
403 | // 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some |
---|
404 | // resources may just contain a bundle of code and never formally define a module via define |
---|
405 | // |
---|
406 | // 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result. |
---|
407 | = {}, |
---|
408 | |
---|
409 | cacheBust |
---|
410 | // query string to append to module URLs to bust browser cache |
---|
411 | = "", |
---|
412 | |
---|
413 | cache |
---|
414 | // hash:(mid | url)-->(function | string) |
---|
415 | // |
---|
416 | // A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or |
---|
417 | // url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided |
---|
418 | // by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided |
---|
419 | // by configl.cache always have a function value that causes the same code to execute as if the module was script injected. |
---|
420 | // |
---|
421 | // Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given |
---|
422 | // by any mappings *iff* the config.cache was received as part of a module resource request. |
---|
423 | // |
---|
424 | // Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules |
---|
425 | // to retrieve cached items that may have arrived consequent to another namespace. |
---|
426 | // |
---|
427 | = {}, |
---|
428 | |
---|
429 | urlKeyPrefix |
---|
430 | // the prefix to prepend to a URL key in the cache. |
---|
431 | = "url:", |
---|
432 | |
---|
433 | pendingCacheInsert |
---|
434 | // hash:(mid)-->(function) |
---|
435 | // |
---|
436 | // Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are |
---|
437 | // entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another |
---|
438 | // independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending |
---|
439 | // cache for the local configuration, possibly relocating modules. |
---|
440 | = {}, |
---|
441 | |
---|
442 | dojoSniffConfig |
---|
443 | // map of configuration variables |
---|
444 | // give the data-dojo-config as sniffed from the document (if any) |
---|
445 | = {}, |
---|
446 | |
---|
447 | insertPointSibling |
---|
448 | // the nodes used to locate where scripts are injected into the document |
---|
449 | = 0; |
---|
450 | |
---|
451 | if(has("dojo-config-api")){ |
---|
452 | var consumePendingCacheInsert = function(referenceModule){ |
---|
453 | var p, item, match, now, m; |
---|
454 | for(p in pendingCacheInsert){ |
---|
455 | item = pendingCacheInsert[p]; |
---|
456 | match = p.match(/^url\:(.+)/); |
---|
457 | if(match){ |
---|
458 | cache[urlKeyPrefix + toUrl(match[1], referenceModule)] = item; |
---|
459 | }else if(p=="*now"){ |
---|
460 | now = item; |
---|
461 | }else if(p!="*noref"){ |
---|
462 | m = getModuleInfo(p, referenceModule, true); |
---|
463 | cache[m.mid] = cache[urlKeyPrefix + m.url] = item; |
---|
464 | } |
---|
465 | } |
---|
466 | if(now){ |
---|
467 | now(createRequire(referenceModule)); |
---|
468 | } |
---|
469 | pendingCacheInsert = {}; |
---|
470 | }, |
---|
471 | |
---|
472 | escapeString = function(s){ |
---|
473 | return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; }); |
---|
474 | }, |
---|
475 | |
---|
476 | computeMapProg = function(map, dest){ |
---|
477 | // This routine takes a map as represented by a JavaScript object and initializes dest, a vector of |
---|
478 | // quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length- |
---|
479 | // of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning |
---|
480 | // of a the search source. Notice the map-value is irrelevant to the algorithm |
---|
481 | dest.splice(0, dest.length); |
---|
482 | for(var p in map){ |
---|
483 | dest.push([ |
---|
484 | p, |
---|
485 | map[p], |
---|
486 | new RegExp("^" + escapeString(p) + "(\/|$)"), |
---|
487 | p.length]); |
---|
488 | } |
---|
489 | dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; }); |
---|
490 | return dest; |
---|
491 | }, |
---|
492 | |
---|
493 | computeAliases = function(config, dest){ |
---|
494 | forEach(config, function(pair){ |
---|
495 | // take a fixed-up copy... |
---|
496 | dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0], pair[1]]); |
---|
497 | }); |
---|
498 | }, |
---|
499 | |
---|
500 | |
---|
501 | fixupPackageInfo = function(packageInfo){ |
---|
502 | // calculate the precise (name, location, main, mappings) for a package |
---|
503 | var name = packageInfo.name; |
---|
504 | if(!name){ |
---|
505 | // packageInfo must be a string that gives the name |
---|
506 | name = packageInfo; |
---|
507 | packageInfo = {name:name}; |
---|
508 | } |
---|
509 | packageInfo = mix({main:"main"}, packageInfo); |
---|
510 | packageInfo.location = packageInfo.location ? packageInfo.location : name; |
---|
511 | |
---|
512 | // packageMap is deprecated in favor of AMD map |
---|
513 | if(packageInfo.packageMap){ |
---|
514 | map[name] = packageInfo.packageMap; |
---|
515 | } |
---|
516 | |
---|
517 | if(!packageInfo.main.indexOf("./")){ |
---|
518 | packageInfo.main = packageInfo.main.substring(2); |
---|
519 | } |
---|
520 | |
---|
521 | // now that we've got a fully-resolved package object, push it into the configuration |
---|
522 | packs[name] = packageInfo; |
---|
523 | }, |
---|
524 | |
---|
525 | delayedModuleConfig |
---|
526 | // module config cannot be consumed until the loader is completely initialized; therefore, all |
---|
527 | // module config detected during booting is memorized and applied at the end of loader initialization |
---|
528 | // TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but |
---|
529 | // we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete |
---|
530 | = [], |
---|
531 | |
---|
532 | |
---|
533 | config = function(config, booting, referenceModule){ |
---|
534 | for(var p in config){ |
---|
535 | if(p=="waitSeconds"){ |
---|
536 | req.waitms = (config[p] || 0) * 1000; |
---|
537 | } |
---|
538 | if(p=="cacheBust"){ |
---|
539 | cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; |
---|
540 | } |
---|
541 | if(p=="baseUrl" || p=="combo"){ |
---|
542 | req[p] = config[p]; |
---|
543 | } |
---|
544 | if(has("dojo-sync-loader") && p=="async"){ |
---|
545 | // falsy or "sync" => legacy sync loader |
---|
546 | // "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader) |
---|
547 | // "legacyAsync" => permanently in "xd" by choice |
---|
548 | // "debugAtAllCosts" => trying to load everything via script injection (not implemented) |
---|
549 | // otherwise, must be truthy => AMD |
---|
550 | // legacyMode: sync | legacyAsync | xd | false |
---|
551 | var mode = config[p]; |
---|
552 | req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false)); |
---|
553 | req.async = !legacyMode; |
---|
554 | } |
---|
555 | if(config[p]!==hasCache){ |
---|
556 | // accumulate raw config info for client apps which can use this to pass their own config |
---|
557 | req.rawConfig[p] = config[p]; |
---|
558 | p!="has" && has.add("config-"+p, config[p], 0, booting); |
---|
559 | } |
---|
560 | } |
---|
561 | |
---|
562 | // make sure baseUrl exists |
---|
563 | if(!req.baseUrl){ |
---|
564 | req.baseUrl = "./"; |
---|
565 | } |
---|
566 | // make sure baseUrl ends with a slash |
---|
567 | if(!/\/$/.test(req.baseUrl)){ |
---|
568 | req.baseUrl += "/"; |
---|
569 | } |
---|
570 | |
---|
571 | // now do the special work for has, packages, packagePaths, paths, aliases, and cache |
---|
572 | |
---|
573 | for(p in config.has){ |
---|
574 | has.add(p, config.has[p], 0, booting); |
---|
575 | } |
---|
576 | |
---|
577 | // for each package found in any packages config item, augment the packs map owned by the loader |
---|
578 | forEach(config.packages, fixupPackageInfo); |
---|
579 | |
---|
580 | // for each packagePath found in any packagePaths config item, augment the packageConfig |
---|
581 | // packagePaths is deprecated; remove in 2.0 |
---|
582 | for(baseUrl in config.packagePaths){ |
---|
583 | forEach(config.packagePaths[baseUrl], function(packageInfo){ |
---|
584 | var location = baseUrl + "/" + packageInfo; |
---|
585 | if(isString(packageInfo)){ |
---|
586 | packageInfo = {name:packageInfo}; |
---|
587 | } |
---|
588 | packageInfo.location = location; |
---|
589 | fixupPackageInfo(packageInfo); |
---|
590 | }); |
---|
591 | } |
---|
592 | |
---|
593 | // notice that computeMapProg treats the dest as a reference; therefore, if/when that variable |
---|
594 | // is published (see dojo-publish-privates), the published variable will always hold a valid value. |
---|
595 | |
---|
596 | // this must come after all package processing since package processing may mutate map |
---|
597 | computeMapProg(mix(map, config.map), mapProgs); |
---|
598 | forEach(mapProgs, function(item){ |
---|
599 | item[1] = computeMapProg(item[1], []); |
---|
600 | if(item[0]=="*"){ |
---|
601 | mapProgs.star = item; |
---|
602 | } |
---|
603 | }); |
---|
604 | |
---|
605 | // push in any paths and recompute the internal pathmap |
---|
606 | computeMapProg(mix(paths, config.paths), pathsMapProg); |
---|
607 | |
---|
608 | // aliases |
---|
609 | computeAliases(config.aliases, aliases); |
---|
610 | |
---|
611 | if(booting){ |
---|
612 | delayedModuleConfig.push({config:config.config}); |
---|
613 | }else{ |
---|
614 | for(p in config.config){ |
---|
615 | var module = getModule(p, referenceModule); |
---|
616 | module.config = mix(module.config || {}, config.config[p]); |
---|
617 | } |
---|
618 | } |
---|
619 | |
---|
620 | // push in any new cache values |
---|
621 | if(config.cache){ |
---|
622 | consumePendingCacheInsert(); |
---|
623 | pendingCacheInsert = config.cache; |
---|
624 | if(config.cache["*noref"]){ |
---|
625 | consumePendingCacheInsert(); |
---|
626 | } |
---|
627 | } |
---|
628 | |
---|
629 | signal("config", [config, req.rawConfig]); |
---|
630 | }; |
---|
631 | |
---|
632 | // |
---|
633 | // execute the various sniffs; userConfig can override and value |
---|
634 | // |
---|
635 | |
---|
636 | if(has("dojo-cdn") || has("dojo-sniff")){ |
---|
637 | // the sniff regex looks for a src attribute ending in dojo.js, optionally preceded with a path. |
---|
638 | // match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the |
---|
639 | // dojo location on CDN deployments and baseUrl when either/both of these are not provided |
---|
640 | // explicitly in the config data; this is the 1.6- behavior. |
---|
641 | |
---|
642 | var scripts = doc.getElementsByTagName("script"), |
---|
643 | i = 0, |
---|
644 | script, dojoDir, src, match; |
---|
645 | while(i < scripts.length){ |
---|
646 | script = scripts[i++]; |
---|
647 | if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){ |
---|
648 | // sniff dojoDir and baseUrl |
---|
649 | dojoDir = match[3] || ""; |
---|
650 | defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir; |
---|
651 | |
---|
652 | // remember an insertPointSibling |
---|
653 | insertPointSibling = script; |
---|
654 | } |
---|
655 | |
---|
656 | // sniff configuration on attribute in script element |
---|
657 | if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){ |
---|
658 | dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config"); |
---|
659 | |
---|
660 | // remember an insertPointSibling |
---|
661 | insertPointSibling = script; |
---|
662 | } |
---|
663 | |
---|
664 | // sniff requirejs attribute |
---|
665 | if(has("dojo-requirejs-api")){ |
---|
666 | if((src = script.getAttribute("data-main"))){ |
---|
667 | dojoSniffConfig.deps = dojoSniffConfig.deps || [src]; |
---|
668 | } |
---|
669 | } |
---|
670 | } |
---|
671 | } |
---|
672 | |
---|
673 | if(has("dojo-test-sniff")){ |
---|
674 | // pass down doh.testConfig from parent as if it were a data-dojo-config |
---|
675 | try{ |
---|
676 | if(window.parent != window && window.parent.require){ |
---|
677 | var doh = window.parent.require("doh"); |
---|
678 | doh && mix(dojoSniffConfig, doh.testConfig); |
---|
679 | } |
---|
680 | }catch(e){} |
---|
681 | } |
---|
682 | |
---|
683 | // configure the loader; let the user override defaults |
---|
684 | req.rawConfig = {}; |
---|
685 | config(defaultConfig, 1); |
---|
686 | |
---|
687 | // do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides |
---|
688 | if(has("dojo-cdn")){ |
---|
689 | packs.dojo.location = dojoDir; |
---|
690 | if(dojoDir){ |
---|
691 | dojoDir += "/"; |
---|
692 | } |
---|
693 | packs.dijit.location = dojoDir + "../dijit/"; |
---|
694 | packs.dojox.location = dojoDir + "../dojox/"; |
---|
695 | } |
---|
696 | |
---|
697 | config(userConfig, 1); |
---|
698 | config(dojoSniffConfig, 1); |
---|
699 | |
---|
700 | }else{ |
---|
701 | // no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application |
---|
702 | paths = defaultConfig.paths; |
---|
703 | pathsMapProg = defaultConfig.pathsMapProg; |
---|
704 | packs = defaultConfig.packs; |
---|
705 | aliases = defaultConfig.aliases; |
---|
706 | mapProgs = defaultConfig.mapProgs; |
---|
707 | modules = defaultConfig.modules; |
---|
708 | cache = defaultConfig.cache; |
---|
709 | cacheBust = defaultConfig.cacheBust; |
---|
710 | |
---|
711 | // remember the default config for other processes (e.g., dojo/config) |
---|
712 | req.rawConfig = defaultConfig; |
---|
713 | } |
---|
714 | |
---|
715 | |
---|
716 | if(has("dojo-combo-api")){ |
---|
717 | req.combo = req.combo || {add:noop}; |
---|
718 | var comboPending = 0, |
---|
719 | combosPending = [], |
---|
720 | comboPendingTimer = null; |
---|
721 | } |
---|
722 | |
---|
723 | |
---|
724 | // build the loader machinery iaw configuration, including has feature tests |
---|
725 | var injectDependencies = function(module){ |
---|
726 | // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies |
---|
727 | guardCheckComplete(function(){ |
---|
728 | forEach(module.deps, injectModule); |
---|
729 | if(has("dojo-combo-api") && comboPending && !comboPendingTimer){ |
---|
730 | comboPendingTimer = setTimeout(function() { |
---|
731 | comboPending = 0; |
---|
732 | comboPendingTimer = null; |
---|
733 | req.combo.done(function(mids, url) { |
---|
734 | var onLoadCallback= function(){ |
---|
735 | // defQ is a vector of module definitions 1-to-1, onto mids |
---|
736 | runDefQ(0, mids); |
---|
737 | checkComplete(); |
---|
738 | }; |
---|
739 | combosPending.push(mids); |
---|
740 | injectingModule = mids; |
---|
741 | req.injectUrl(url, onLoadCallback, mids); |
---|
742 | injectingModule = 0; |
---|
743 | }, req); |
---|
744 | }, 0); |
---|
745 | } |
---|
746 | }); |
---|
747 | }, |
---|
748 | |
---|
749 | contextRequire = function(a1, a2, a3, referenceModule, contextRequire){ |
---|
750 | var module, syntheticMid; |
---|
751 | if(isString(a1)){ |
---|
752 | // signature is (moduleId) |
---|
753 | module = getModule(a1, referenceModule, true); |
---|
754 | if(module && module.executed){ |
---|
755 | return module.result; |
---|
756 | } |
---|
757 | throw makeError("undefinedModule", a1); |
---|
758 | } |
---|
759 | if(!isArray(a1)){ |
---|
760 | // a1 is a configuration |
---|
761 | config(a1, 0, referenceModule); |
---|
762 | |
---|
763 | // juggle args; (a2, a3) may be (dependencies, callback) |
---|
764 | a1 = a2; |
---|
765 | a2 = a3; |
---|
766 | } |
---|
767 | if(isArray(a1)){ |
---|
768 | // signature is (requestList [,callback]) |
---|
769 | if(!a1.length){ |
---|
770 | a2 && a2(); |
---|
771 | }else{ |
---|
772 | syntheticMid = "require*" + uid(); |
---|
773 | |
---|
774 | // resolve the request list with respect to the reference module |
---|
775 | for(var mid, deps = [], i = 0; i < a1.length;){ |
---|
776 | mid = a1[i++]; |
---|
777 | deps.push(getModule(mid, referenceModule)); |
---|
778 | } |
---|
779 | |
---|
780 | // construct a synthetic module to control execution of the requestList, and, optionally, callback |
---|
781 | module = mix(makeModuleInfo("", syntheticMid, 0, ""), { |
---|
782 | injected: arrived, |
---|
783 | deps: deps, |
---|
784 | def: a2 || noop, |
---|
785 | require: referenceModule ? referenceModule.require : req, |
---|
786 | gc: 1 //garbage collect |
---|
787 | }); |
---|
788 | modules[module.mid] = module; |
---|
789 | |
---|
790 | // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies |
---|
791 | injectDependencies(module); |
---|
792 | |
---|
793 | // try to immediately execute |
---|
794 | // if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe |
---|
795 | // it's possible to execute this require later after the current traversal completes and avoid the circular dependency. |
---|
796 | // ...but *always* insist on immediate in synch mode |
---|
797 | var strict = checkCompleteGuard && legacyMode!=sync; |
---|
798 | guardCheckComplete(function(){ |
---|
799 | execModule(module, strict); |
---|
800 | }); |
---|
801 | if(!module.executed){ |
---|
802 | // some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ |
---|
803 | execQ.push(module); |
---|
804 | } |
---|
805 | checkComplete(); |
---|
806 | } |
---|
807 | } |
---|
808 | return contextRequire; |
---|
809 | }, |
---|
810 | |
---|
811 | createRequire = function(module){ |
---|
812 | if(!module){ |
---|
813 | return req; |
---|
814 | } |
---|
815 | var result = module.require; |
---|
816 | if(!result){ |
---|
817 | result = function(a1, a2, a3){ |
---|
818 | return contextRequire(a1, a2, a3, module, result); |
---|
819 | }; |
---|
820 | module.require = mix(result, req); |
---|
821 | result.module = module; |
---|
822 | result.toUrl = function(name){ |
---|
823 | return toUrl(name, module); |
---|
824 | }; |
---|
825 | result.toAbsMid = function(mid){ |
---|
826 | return toAbsMid(mid, module); |
---|
827 | }; |
---|
828 | if(has("dojo-undef-api")){ |
---|
829 | result.undef = function(mid){ |
---|
830 | req.undef(mid, module); |
---|
831 | }; |
---|
832 | } |
---|
833 | if(has("dojo-sync-loader")){ |
---|
834 | result.syncLoadNls = function(mid){ |
---|
835 | var nlsModuleInfo = getModuleInfo(mid, module), |
---|
836 | nlsModule = modules[nlsModuleInfo.mid]; |
---|
837 | if(!nlsModule || !nlsModule.executed){ |
---|
838 | cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url]; |
---|
839 | if(cached){ |
---|
840 | evalModuleText(cached); |
---|
841 | nlsModule = modules[nlsModuleInfo.mid]; |
---|
842 | } |
---|
843 | } |
---|
844 | return nlsModule && nlsModule.executed && nlsModule.result; |
---|
845 | }; |
---|
846 | } |
---|
847 | |
---|
848 | } |
---|
849 | return result; |
---|
850 | }, |
---|
851 | |
---|
852 | execQ = |
---|
853 | // The list of modules that need to be evaluated. |
---|
854 | [], |
---|
855 | |
---|
856 | defQ = |
---|
857 | // The queue of define arguments sent to loader. |
---|
858 | [], |
---|
859 | |
---|
860 | waiting = |
---|
861 | // The set of modules upon which the loader is waiting for definition to arrive |
---|
862 | {}, |
---|
863 | |
---|
864 | setRequested = function(module){ |
---|
865 | module.injected = requested; |
---|
866 | waiting[module.mid] = 1; |
---|
867 | if(module.url){ |
---|
868 | waiting[module.url] = module.pack || 1; |
---|
869 | } |
---|
870 | startTimer(); |
---|
871 | }, |
---|
872 | |
---|
873 | setArrived = function(module){ |
---|
874 | module.injected = arrived; |
---|
875 | delete waiting[module.mid]; |
---|
876 | if(module.url){ |
---|
877 | delete waiting[module.url]; |
---|
878 | } |
---|
879 | if(isEmpty(waiting)){ |
---|
880 | clearTimer(); |
---|
881 | has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync); |
---|
882 | } |
---|
883 | }, |
---|
884 | |
---|
885 | execComplete = req.idle = |
---|
886 | // says the loader has completed (or not) its work |
---|
887 | function(){ |
---|
888 | return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard; |
---|
889 | }, |
---|
890 | |
---|
891 | runMapProg = function(targetMid, map){ |
---|
892 | // search for targetMid in map; return the map item if found; falsy otherwise |
---|
893 | if(map){ |
---|
894 | for(var i = 0; i < map.length; i++){ |
---|
895 | if(map[i][2].test(targetMid)){ |
---|
896 | return map[i]; |
---|
897 | } |
---|
898 | } |
---|
899 | } |
---|
900 | return 0; |
---|
901 | }, |
---|
902 | |
---|
903 | compactPath = function(path){ |
---|
904 | var result = [], |
---|
905 | segment, lastSegment; |
---|
906 | path = path.replace(/\\/g, '/').split('/'); |
---|
907 | while(path.length){ |
---|
908 | segment = path.shift(); |
---|
909 | if(segment==".." && result.length && lastSegment!=".."){ |
---|
910 | result.pop(); |
---|
911 | lastSegment = result[result.length - 1]; |
---|
912 | }else if(segment!="."){ |
---|
913 | result.push(lastSegment= segment); |
---|
914 | } // else ignore "." |
---|
915 | } |
---|
916 | return result.join("/"); |
---|
917 | }, |
---|
918 | |
---|
919 | makeModuleInfo = function(pid, mid, pack, url){ |
---|
920 | if(has("dojo-sync-loader")){ |
---|
921 | var xd= req.isXdUrl(url); |
---|
922 | return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))}; |
---|
923 | }else{ |
---|
924 | return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0}; |
---|
925 | } |
---|
926 | }, |
---|
927 | |
---|
928 | getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate){ |
---|
929 | // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder) |
---|
930 | // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader |
---|
931 | var pid, pack, midInPackage, mapItem, url, result, isRelative, requestedMid; |
---|
932 | requestedMid = mid; |
---|
933 | isRelative = /^\./.test(mid); |
---|
934 | if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){ |
---|
935 | // absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page |
---|
936 | // whatever it is, it's not a module but just a URL of some sort |
---|
937 | // note: pid===0 indicates the routine is returning an unmodified mid |
---|
938 | |
---|
939 | return makeModuleInfo(0, mid, 0, mid); |
---|
940 | }else{ |
---|
941 | // relative module ids are relative to the referenceModule; get rid of any dots |
---|
942 | mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid); |
---|
943 | if(/^\./.test(mid)){ |
---|
944 | throw makeError("irrationalPath", mid); |
---|
945 | } |
---|
946 | // at this point, mid is an absolute mid |
---|
947 | |
---|
948 | // map the mid |
---|
949 | if(referenceModule){ |
---|
950 | mapItem = runMapProg(referenceModule.mid, mapProgs); |
---|
951 | } |
---|
952 | mapItem = mapItem || mapProgs.star; |
---|
953 | mapItem = mapItem && runMapProg(mid, mapItem[1]); |
---|
954 | |
---|
955 | if(mapItem){ |
---|
956 | mid = mapItem[1] + mid.substring(mapItem[3]); |
---|
957 | } |
---|
958 | |
---|
959 | match = mid.match(/^([^\/]+)(\/(.+))?$/); |
---|
960 | pid = match ? match[1] : ""; |
---|
961 | if((pack = packs[pid])){ |
---|
962 | mid = pid + "/" + (midInPackage = (match[3] || pack.main)); |
---|
963 | }else{ |
---|
964 | pid = ""; |
---|
965 | } |
---|
966 | |
---|
967 | // search aliases |
---|
968 | var candidateLength = 0, |
---|
969 | candidate = 0; |
---|
970 | forEach(aliases, function(pair){ |
---|
971 | var match = mid.match(pair[0]); |
---|
972 | if(match && match.length>candidateLength){ |
---|
973 | candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1]; |
---|
974 | } |
---|
975 | }); |
---|
976 | if(candidate){ |
---|
977 | return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate); |
---|
978 | } |
---|
979 | |
---|
980 | result = modules[mid]; |
---|
981 | if(result){ |
---|
982 | return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid]; |
---|
983 | } |
---|
984 | } |
---|
985 | // get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the |
---|
986 | // fully resolved (i.e., all relative indicators and package mapping resolved) module id |
---|
987 | |
---|
988 | // note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid |
---|
989 | mapItem = runMapProg(mid, pathsMapProg); |
---|
990 | if(mapItem){ |
---|
991 | url = mapItem[1] + mid.substring(mapItem[3]); |
---|
992 | }else if(pid){ |
---|
993 | url = pack.location + "/" + midInPackage; |
---|
994 | }else if(has("config-tlmSiblingOfDojo")){ |
---|
995 | url = "../" + mid; |
---|
996 | }else{ |
---|
997 | url = mid; |
---|
998 | } |
---|
999 | // if result is not absolute, add baseUrl |
---|
1000 | if(!(/(^\/)|(\:)/.test(url))){ |
---|
1001 | url = baseUrl + url; |
---|
1002 | } |
---|
1003 | url += ".js"; |
---|
1004 | return makeModuleInfo(pid, mid, pack, compactPath(url)); |
---|
1005 | }, |
---|
1006 | |
---|
1007 | getModuleInfo = function(mid, referenceModule, fromPendingCache){ |
---|
1008 | return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, fromPendingCache ? [] : mapProgs, fromPendingCache ? [] : pathsMapProg, fromPendingCache ? [] : aliases); |
---|
1009 | }, |
---|
1010 | |
---|
1011 | resolvePluginResourceId = function(plugin, prid, referenceModule){ |
---|
1012 | return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule); |
---|
1013 | }, |
---|
1014 | |
---|
1015 | dynamicPluginUidGenerator = 0, |
---|
1016 | |
---|
1017 | getModule = function(mid, referenceModule, immediate){ |
---|
1018 | // compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule |
---|
1019 | var match, plugin, prid, result; |
---|
1020 | match = mid.match(/^(.+?)\!(.*)$/); |
---|
1021 | if(match){ |
---|
1022 | // name was <plugin-module>!<plugin-resource-id> |
---|
1023 | plugin = getModule(match[1], referenceModule, immediate); |
---|
1024 | |
---|
1025 | if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){ |
---|
1026 | injectModule(plugin); |
---|
1027 | if(plugin.injected===arrived && !plugin.executed){ |
---|
1028 | guardCheckComplete(function(){ |
---|
1029 | execModule(plugin); |
---|
1030 | }); |
---|
1031 | } |
---|
1032 | if(plugin.executed){ |
---|
1033 | promoteModuleToPlugin(plugin); |
---|
1034 | }else{ |
---|
1035 | // we are in xdomain mode for some reason |
---|
1036 | execQ.unshift(plugin); |
---|
1037 | } |
---|
1038 | } |
---|
1039 | |
---|
1040 | |
---|
1041 | |
---|
1042 | if(plugin.executed === executed && !plugin.load){ |
---|
1043 | // executed the module not knowing it was a plugin |
---|
1044 | promoteModuleToPlugin(plugin); |
---|
1045 | } |
---|
1046 | |
---|
1047 | // if the plugin has not been loaded, then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise |
---|
1048 | if(plugin.load){ |
---|
1049 | prid = resolvePluginResourceId(plugin, match[2], referenceModule); |
---|
1050 | mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid); |
---|
1051 | }else{ |
---|
1052 | prid = match[2]; |
---|
1053 | mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin"; |
---|
1054 | } |
---|
1055 | result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid}; |
---|
1056 | }else{ |
---|
1057 | result = getModuleInfo(mid, referenceModule); |
---|
1058 | } |
---|
1059 | return modules[result.mid] || (!immediate && (modules[result.mid] = result)); |
---|
1060 | }, |
---|
1061 | |
---|
1062 | toAbsMid = req.toAbsMid = function(mid, referenceModule){ |
---|
1063 | return getModuleInfo(mid, referenceModule).mid; |
---|
1064 | }, |
---|
1065 | |
---|
1066 | toUrl = req.toUrl = function(name, referenceModule){ |
---|
1067 | var moduleInfo = getModuleInfo(name+"/x", referenceModule), |
---|
1068 | url= moduleInfo.url; |
---|
1069 | return fixupUrl(moduleInfo.pid===0 ? |
---|
1070 | // if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases |
---|
1071 | name : |
---|
1072 | // "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id |
---|
1073 | url.substring(0, url.length-5) |
---|
1074 | ); |
---|
1075 | }, |
---|
1076 | |
---|
1077 | nonModuleProps = { |
---|
1078 | injected: arrived, |
---|
1079 | executed: executed, |
---|
1080 | def: nonmodule, |
---|
1081 | result: nonmodule |
---|
1082 | }, |
---|
1083 | |
---|
1084 | makeCjs = function(mid){ |
---|
1085 | return modules[mid] = mix({mid:mid}, nonModuleProps); |
---|
1086 | }, |
---|
1087 | |
---|
1088 | cjsRequireModule = makeCjs("require"), |
---|
1089 | cjsExportsModule = makeCjs("exports"), |
---|
1090 | cjsModuleModule = makeCjs("module"), |
---|
1091 | |
---|
1092 | runFactory = function(module, args){ |
---|
1093 | req.trace("loader-run-factory", [module.mid]); |
---|
1094 | var factory = module.def, |
---|
1095 | result; |
---|
1096 | has("dojo-sync-loader") && syncExecStack.unshift(module); |
---|
1097 | if(has("config-dojo-loader-catches")){ |
---|
1098 | try{ |
---|
1099 | result= isFunction(factory) ? factory.apply(null, args) : factory; |
---|
1100 | }catch(e){ |
---|
1101 | signal(error, module.result = makeError("factoryThrew", [module, e])); |
---|
1102 | } |
---|
1103 | }else{ |
---|
1104 | result= isFunction(factory) ? factory.apply(null, args) : factory; |
---|
1105 | } |
---|
1106 | module.result = result===undefined && module.cjs ? module.cjs.exports : result; |
---|
1107 | has("dojo-sync-loader") && syncExecStack.shift(module); |
---|
1108 | }, |
---|
1109 | |
---|
1110 | abortExec = {}, |
---|
1111 | |
---|
1112 | defOrder = 0, |
---|
1113 | |
---|
1114 | promoteModuleToPlugin = function(pluginModule){ |
---|
1115 | var plugin = pluginModule.result; |
---|
1116 | pluginModule.dynamic = plugin.dynamic; |
---|
1117 | pluginModule.normalize = plugin.normalize; |
---|
1118 | pluginModule.load = plugin.load; |
---|
1119 | return pluginModule; |
---|
1120 | }, |
---|
1121 | |
---|
1122 | resolvePluginLoadQ = function(plugin){ |
---|
1123 | // plugins is a newly executed module that has a loadQ waiting to run |
---|
1124 | |
---|
1125 | // step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid |
---|
1126 | // recall the original mid was created before the plugin was on board and therefore it was impossible to |
---|
1127 | // compute the final mid; accordingly, prid may or may not change, but the mid will definitely change |
---|
1128 | var map = {}; |
---|
1129 | forEach(plugin.loadQ, function(pseudoPluginResource){ |
---|
1130 | // manufacture and insert the real module in modules |
---|
1131 | var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module), |
---|
1132 | mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid), |
---|
1133 | pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0}); |
---|
1134 | if(!modules[mid]){ |
---|
1135 | // create a new (the real) plugin resource and inject it normally now that the plugin is on board |
---|
1136 | injectPlugin(modules[mid] = pluginResource); |
---|
1137 | } // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin |
---|
1138 | |
---|
1139 | // pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board) |
---|
1140 | // mark is as arrived and delete it from modules; the real module was requested above |
---|
1141 | map[pseudoPluginResource.mid] = modules[mid]; |
---|
1142 | setArrived(pseudoPluginResource); |
---|
1143 | delete modules[pseudoPluginResource.mid]; |
---|
1144 | }); |
---|
1145 | plugin.loadQ = 0; |
---|
1146 | |
---|
1147 | // step2: replace all references to any placeholder modules with real modules |
---|
1148 | var substituteModules = function(module){ |
---|
1149 | for(var replacement, deps = module.deps || [], i = 0; i<deps.length; i++){ |
---|
1150 | replacement = map[deps[i].mid]; |
---|
1151 | if(replacement){ |
---|
1152 | deps[i] = replacement; |
---|
1153 | } |
---|
1154 | } |
---|
1155 | }; |
---|
1156 | for(var p in modules){ |
---|
1157 | substituteModules(modules[p]); |
---|
1158 | } |
---|
1159 | forEach(execQ, substituteModules); |
---|
1160 | }, |
---|
1161 | |
---|
1162 | finishExec = function(module){ |
---|
1163 | req.trace("loader-finish-exec", [module.mid]); |
---|
1164 | module.executed = executed; |
---|
1165 | module.defOrder = defOrder++; |
---|
1166 | has("dojo-sync-loader") && forEach(module.provides, function(cb){ cb(); }); |
---|
1167 | if(module.loadQ){ |
---|
1168 | // the module was a plugin |
---|
1169 | promoteModuleToPlugin(module); |
---|
1170 | resolvePluginLoadQ(module); |
---|
1171 | } |
---|
1172 | // remove all occurrences of this module from the execQ |
---|
1173 | for(i = 0; i < execQ.length;){ |
---|
1174 | if(execQ[i] === module){ |
---|
1175 | execQ.splice(i, 1); |
---|
1176 | }else{ |
---|
1177 | i++; |
---|
1178 | } |
---|
1179 | } |
---|
1180 | // delete references to synthetic modules |
---|
1181 | if (/^require\*/.test(module.mid)) { |
---|
1182 | delete modules[module.mid]; |
---|
1183 | } |
---|
1184 | }, |
---|
1185 | |
---|
1186 | circleTrace = [], |
---|
1187 | |
---|
1188 | execModule = function(module, strict){ |
---|
1189 | // run the dependency vector, then run the factory for module |
---|
1190 | if(module.executed === executing){ |
---|
1191 | req.trace("loader-circular-dependency", [circleTrace.concat(module.mid).join("->")]); |
---|
1192 | return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports); |
---|
1193 | } |
---|
1194 | // at this point the module is either not executed or fully executed |
---|
1195 | |
---|
1196 | |
---|
1197 | if(!module.executed){ |
---|
1198 | if(!module.def){ |
---|
1199 | return abortExec; |
---|
1200 | } |
---|
1201 | var mid = module.mid, |
---|
1202 | deps = module.deps || [], |
---|
1203 | arg, argResult, |
---|
1204 | args = [], |
---|
1205 | i = 0; |
---|
1206 | |
---|
1207 | if(has("dojo-trace-api")){ |
---|
1208 | circleTrace.push(mid); |
---|
1209 | req.trace("loader-exec-module", ["exec", circleTrace.length, mid]); |
---|
1210 | } |
---|
1211 | |
---|
1212 | // for circular dependencies, assume the first module encountered was executed OK |
---|
1213 | // modules that circularly depend on a module that has not run its factory will get |
---|
1214 | // the pre-made cjs.exports===module.result. They can take a reference to this object and/or |
---|
1215 | // add properties to it. When the module finally runs its factory, the factory can |
---|
1216 | // read/write/replace this object. Notice that so long as the object isn't replaced, any |
---|
1217 | // reference taken earlier while walking the deps list is still valid. |
---|
1218 | module.executed = executing; |
---|
1219 | while((arg = deps[i++])){ |
---|
1220 | argResult = ((arg === cjsRequireModule) ? createRequire(module) : |
---|
1221 | ((arg === cjsExportsModule) ? module.cjs.exports : |
---|
1222 | ((arg === cjsModuleModule) ? module.cjs : |
---|
1223 | execModule(arg, strict)))); |
---|
1224 | if(argResult === abortExec){ |
---|
1225 | module.executed = 0; |
---|
1226 | req.trace("loader-exec-module", ["abort", mid]); |
---|
1227 | has("dojo-trace-api") && circleTrace.pop(); |
---|
1228 | return abortExec; |
---|
1229 | } |
---|
1230 | args.push(argResult); |
---|
1231 | } |
---|
1232 | runFactory(module, args); |
---|
1233 | finishExec(module); |
---|
1234 | has("dojo-trace-api") && circleTrace.pop(); |
---|
1235 | } |
---|
1236 | // at this point the module is guaranteed fully executed |
---|
1237 | |
---|
1238 | return module.result; |
---|
1239 | }, |
---|
1240 | |
---|
1241 | |
---|
1242 | checkCompleteGuard = 0, |
---|
1243 | |
---|
1244 | guardCheckComplete = function(proc){ |
---|
1245 | try{ |
---|
1246 | checkCompleteGuard++; |
---|
1247 | proc(); |
---|
1248 | }finally{ |
---|
1249 | checkCompleteGuard--; |
---|
1250 | } |
---|
1251 | if(execComplete()){ |
---|
1252 | signal("idle", []); |
---|
1253 | } |
---|
1254 | }, |
---|
1255 | |
---|
1256 | checkComplete = function(){ |
---|
1257 | // keep going through the execQ as long as at least one factory is executed |
---|
1258 | // plugins, recursion, cached modules all make for many execution path possibilities |
---|
1259 | if(checkCompleteGuard){ |
---|
1260 | return; |
---|
1261 | } |
---|
1262 | guardCheckComplete(function(){ |
---|
1263 | checkDojoRequirePlugin(); |
---|
1264 | for(var currentDefOrder, module, i = 0; i < execQ.length;){ |
---|
1265 | currentDefOrder = defOrder; |
---|
1266 | module = execQ[i]; |
---|
1267 | execModule(module); |
---|
1268 | if(currentDefOrder!=defOrder){ |
---|
1269 | // defOrder was bumped one or more times indicating something was executed (note, this indicates |
---|
1270 | // the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute) |
---|
1271 | checkDojoRequirePlugin(); |
---|
1272 | i = 0; |
---|
1273 | }else{ |
---|
1274 | // nothing happened; check the next module in the exec queue |
---|
1275 | i++; |
---|
1276 | } |
---|
1277 | } |
---|
1278 | }); |
---|
1279 | }; |
---|
1280 | |
---|
1281 | |
---|
1282 | if(has("dojo-undef-api")){ |
---|
1283 | req.undef = function(moduleId, referenceModule){ |
---|
1284 | // In order to reload a module, it must be undefined (this routine) and then re-requested. |
---|
1285 | // This is useful for testing frameworks (at least). |
---|
1286 | var module = getModule(moduleId, referenceModule); |
---|
1287 | setArrived(module); |
---|
1288 | mix(module, {def:0, executed:0, injected:0, node:0}); |
---|
1289 | }; |
---|
1290 | } |
---|
1291 | |
---|
1292 | if(has("dojo-inject-api")){ |
---|
1293 | if(has("dojo-loader-eval-hint-url")===undefined){ |
---|
1294 | has.add("dojo-loader-eval-hint-url", 1); |
---|
1295 | } |
---|
1296 | |
---|
1297 | var fixupUrl= function(url){ |
---|
1298 | url += ""; // make sure url is a Javascript string (some paths may be a Java string) |
---|
1299 | return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : ""); |
---|
1300 | }, |
---|
1301 | |
---|
1302 | injectPlugin = function( |
---|
1303 | module |
---|
1304 | ){ |
---|
1305 | // injects the plugin module given by module; may have to inject the plugin itself |
---|
1306 | var plugin = module.plugin; |
---|
1307 | |
---|
1308 | if(plugin.executed === executed && !plugin.load){ |
---|
1309 | // executed the module not knowing it was a plugin |
---|
1310 | promoteModuleToPlugin(plugin); |
---|
1311 | } |
---|
1312 | |
---|
1313 | var onLoad = function(def){ |
---|
1314 | module.result = def; |
---|
1315 | setArrived(module); |
---|
1316 | finishExec(module); |
---|
1317 | checkComplete(); |
---|
1318 | }; |
---|
1319 | |
---|
1320 | if(plugin.load){ |
---|
1321 | plugin.load(module.prid, module.req, onLoad); |
---|
1322 | }else if(plugin.loadQ){ |
---|
1323 | plugin.loadQ.push(module); |
---|
1324 | }else{ |
---|
1325 | // the unshift instead of push is important: we don't want plugins to execute as |
---|
1326 | // dependencies of some other module because this may cause circles when the plugin |
---|
1327 | // loadQ is run; also, generally, we want plugins to run early since they may load |
---|
1328 | // several other modules and therefore can potentially unblock many modules |
---|
1329 | plugin.loadQ = [module]; |
---|
1330 | execQ.unshift(plugin); |
---|
1331 | injectModule(plugin); |
---|
1332 | } |
---|
1333 | }, |
---|
1334 | |
---|
1335 | // for IE, injecting a module may result in a recursive execution if the module is in the cache |
---|
1336 | |
---|
1337 | cached = 0, |
---|
1338 | |
---|
1339 | injectingModule = 0, |
---|
1340 | |
---|
1341 | injectingCachedModule = 0, |
---|
1342 | |
---|
1343 | evalModuleText = function(text, module){ |
---|
1344 | // see def() for the injectingCachedModule bracket; it simply causes a short, safe circuit |
---|
1345 | if(has("config-stripStrict")){ |
---|
1346 | text = text.replace(/"use strict"/g, ''); |
---|
1347 | } |
---|
1348 | injectingCachedModule = 1; |
---|
1349 | if(has("config-dojo-loader-catches")){ |
---|
1350 | try{ |
---|
1351 | if(text===cached){ |
---|
1352 | cached.call(null); |
---|
1353 | }else{ |
---|
1354 | req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); |
---|
1355 | } |
---|
1356 | }catch(e){ |
---|
1357 | signal(error, makeError("evalModuleThrew", module)); |
---|
1358 | } |
---|
1359 | }else{ |
---|
1360 | if(text===cached){ |
---|
1361 | cached.call(null); |
---|
1362 | }else{ |
---|
1363 | req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); |
---|
1364 | } |
---|
1365 | } |
---|
1366 | injectingCachedModule = 0; |
---|
1367 | }, |
---|
1368 | |
---|
1369 | injectModule = function(module){ |
---|
1370 | // Inject the module. In the browser environment, this means appending a script element into |
---|
1371 | // the document; in other environments, it means loading a file. |
---|
1372 | // |
---|
1373 | // If in synchronous mode, then get the module synchronously if it's not xdomainLoading. |
---|
1374 | |
---|
1375 | var mid = module.mid, |
---|
1376 | url = module.url; |
---|
1377 | if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){ |
---|
1378 | return; |
---|
1379 | } |
---|
1380 | setRequested(module); |
---|
1381 | |
---|
1382 | if(has("dojo-combo-api")){ |
---|
1383 | var viaCombo = 0; |
---|
1384 | if(module.plugin && module.plugin.isCombo){ |
---|
1385 | // a combo plugin; therefore, must be handled by combo service |
---|
1386 | // the prid should have already been converted to a URL (if required by the plugin) during |
---|
1387 | // the normalize process; in any event, there is no way for the loader to know how to |
---|
1388 | // to the conversion; therefore the third argument is zero |
---|
1389 | req.combo.add(module.plugin.mid, module.prid, 0, req); |
---|
1390 | viaCombo = 1; |
---|
1391 | }else if(!module.plugin){ |
---|
1392 | viaCombo = req.combo.add(0, module.mid, module.url, req); |
---|
1393 | } |
---|
1394 | if(viaCombo){ |
---|
1395 | comboPending= 1; |
---|
1396 | return; |
---|
1397 | } |
---|
1398 | } |
---|
1399 | |
---|
1400 | if(module.plugin){ |
---|
1401 | injectPlugin(module); |
---|
1402 | return; |
---|
1403 | } // else a normal module (not a plugin) |
---|
1404 | |
---|
1405 | |
---|
1406 | var onLoadCallback = function(){ |
---|
1407 | runDefQ(module); |
---|
1408 | if(module.injected !== arrived){ |
---|
1409 | // the script that contained the module arrived and has been executed yet |
---|
1410 | // nothing was added to the defQ (so it wasn't an AMD module) and the module |
---|
1411 | // wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module); |
---|
1412 | // therefore, it must not have been a module; adjust state accordingly |
---|
1413 | if(has("dojo-enforceDefine")){ |
---|
1414 | signal(error, makeError("noDefine", module)); |
---|
1415 | return; |
---|
1416 | } |
---|
1417 | setArrived(module); |
---|
1418 | mix(module, nonModuleProps); |
---|
1419 | req.trace("loader-define-nonmodule", [module.url]); |
---|
1420 | } |
---|
1421 | |
---|
1422 | if(has("dojo-sync-loader") && legacyMode){ |
---|
1423 | // must call checkComplete even in for sync loader because we may be in xdomainLoading mode; |
---|
1424 | // but, if xd loading, then don't call checkComplete until out of the current sync traversal |
---|
1425 | // in order to preserve order of execution of the dojo.required modules |
---|
1426 | !syncExecStack.length && checkComplete(); |
---|
1427 | }else{ |
---|
1428 | checkComplete(); |
---|
1429 | } |
---|
1430 | }; |
---|
1431 | cached = cache[mid] || cache[urlKeyPrefix + module.url]; |
---|
1432 | if(cached){ |
---|
1433 | req.trace("loader-inject", ["cache", module.mid, url]); |
---|
1434 | evalModuleText(cached, module); |
---|
1435 | onLoadCallback(); |
---|
1436 | return; |
---|
1437 | } |
---|
1438 | if(has("dojo-sync-loader") && legacyMode){ |
---|
1439 | if(module.isXd){ |
---|
1440 | // switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false} |
---|
1441 | legacyMode==sync && (legacyMode = xd); |
---|
1442 | // fall through and load via script injection |
---|
1443 | }else if(module.isAmd && legacyMode!=sync){ |
---|
1444 | // fall through and load via script injection |
---|
1445 | }else{ |
---|
1446 | // mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain |
---|
1447 | var xhrCallback = function(text){ |
---|
1448 | if(legacyMode==sync){ |
---|
1449 | // the top of syncExecStack gives the current synchronously executing module; the loader needs |
---|
1450 | // to know this if it has to switch to async loading in the middle of evaluating a legacy module |
---|
1451 | // this happens when a modules dojo.require's a module that must be loaded async because it's xdomain |
---|
1452 | // (using unshift/shift because there is no back() methods for Javascript arrays) |
---|
1453 | syncExecStack.unshift(module); |
---|
1454 | evalModuleText(text, module); |
---|
1455 | syncExecStack.shift(); |
---|
1456 | |
---|
1457 | // maybe the module was an AMD module |
---|
1458 | runDefQ(module); |
---|
1459 | |
---|
1460 | // legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing |
---|
1461 | if(!module.cjs){ |
---|
1462 | setArrived(module); |
---|
1463 | finishExec(module); |
---|
1464 | } |
---|
1465 | |
---|
1466 | if(module.finish){ |
---|
1467 | // while synchronously evaluating this module, dojo.require was applied referencing a module |
---|
1468 | // that had to be loaded async; therefore, the loader stopped answering all dojo.require |
---|
1469 | // requests so they could be answered completely in the correct sequence; module.finish gives |
---|
1470 | // the list of dojo.requires that must be re-applied once all target modules are available; |
---|
1471 | // make a synthetic module to execute the dojo.require's in the correct order |
---|
1472 | |
---|
1473 | // compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module |
---|
1474 | // TODO: can we just leave the module.finish...what's it hurting? |
---|
1475 | var finishMid = mid + "*finish", |
---|
1476 | finish = module.finish; |
---|
1477 | delete module.finish; |
---|
1478 | |
---|
1479 | def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){ |
---|
1480 | forEach(finish, function(mid){ dojo.require(mid); }); |
---|
1481 | }); |
---|
1482 | // unshift, not push, which causes the current traversal to be reattempted from the top |
---|
1483 | execQ.unshift(getModule(finishMid)); |
---|
1484 | } |
---|
1485 | onLoadCallback(); |
---|
1486 | }else{ |
---|
1487 | text = transformToAmd(module, text); |
---|
1488 | if(text){ |
---|
1489 | evalModuleText(text, module); |
---|
1490 | onLoadCallback(); |
---|
1491 | }else{ |
---|
1492 | // if transformToAmd returned falsy, then the module was already AMD and it can be script-injected |
---|
1493 | // do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache) |
---|
1494 | injectingModule = module; |
---|
1495 | req.injectUrl(fixupUrl(url), onLoadCallback, module); |
---|
1496 | injectingModule = 0; |
---|
1497 | } |
---|
1498 | } |
---|
1499 | }; |
---|
1500 | |
---|
1501 | req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]); |
---|
1502 | if(has("config-dojo-loader-catches")){ |
---|
1503 | try{ |
---|
1504 | req.getText(url, legacyMode!=sync, xhrCallback); |
---|
1505 | }catch(e){ |
---|
1506 | signal(error, makeError("xhrInjectFailed", [module, e])); |
---|
1507 | } |
---|
1508 | }else{ |
---|
1509 | req.getText(url, legacyMode!=sync, xhrCallback); |
---|
1510 | } |
---|
1511 | return; |
---|
1512 | } |
---|
1513 | } // else async mode or fell through in xdomain loading mode; either way, load by script injection |
---|
1514 | req.trace("loader-inject", ["script", module.mid, url]); |
---|
1515 | injectingModule = module; |
---|
1516 | req.injectUrl(fixupUrl(url), onLoadCallback, module); |
---|
1517 | injectingModule = 0; |
---|
1518 | }, |
---|
1519 | |
---|
1520 | defineModule = function(module, deps, def){ |
---|
1521 | req.trace("loader-define-module", [module.mid, deps]); |
---|
1522 | |
---|
1523 | if(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){ |
---|
1524 | // the module is a plugin resource loaded by the combo service |
---|
1525 | // note: check for module.plugin should be enough since normal plugin resources should |
---|
1526 | // not follow this path; module.plugin.isCombo is future-proofing belt and suspenders |
---|
1527 | module.result = isFunction(def) ? def() : def; |
---|
1528 | setArrived(module); |
---|
1529 | finishExec(module); |
---|
1530 | return module; |
---|
1531 | } |
---|
1532 | |
---|
1533 | var mid = module.mid; |
---|
1534 | if(module.injected === arrived){ |
---|
1535 | signal(error, makeError("multipleDefine", module)); |
---|
1536 | return module; |
---|
1537 | } |
---|
1538 | mix(module, { |
---|
1539 | deps: deps, |
---|
1540 | def: def, |
---|
1541 | cjs: { |
---|
1542 | id: module.mid, |
---|
1543 | uri: module.url, |
---|
1544 | exports: (module.result = {}), |
---|
1545 | setExports: function(exports){ |
---|
1546 | module.cjs.exports = exports; |
---|
1547 | }, |
---|
1548 | config:function(){ |
---|
1549 | return module.config; |
---|
1550 | } |
---|
1551 | } |
---|
1552 | }); |
---|
1553 | |
---|
1554 | // resolve deps with respect to this module |
---|
1555 | for(var i = 0; deps[i]; i++){ |
---|
1556 | deps[i] = getModule(deps[i], module); |
---|
1557 | } |
---|
1558 | |
---|
1559 | if(has("dojo-sync-loader") && legacyMode && !waiting[mid]){ |
---|
1560 | // the module showed up without being asked for; it was probably in a <script> element |
---|
1561 | injectDependencies(module); |
---|
1562 | execQ.push(module); |
---|
1563 | checkComplete(); |
---|
1564 | } |
---|
1565 | setArrived(module); |
---|
1566 | |
---|
1567 | if(!isFunction(def) && !deps.length){ |
---|
1568 | module.result = def; |
---|
1569 | finishExec(module); |
---|
1570 | } |
---|
1571 | |
---|
1572 | return module; |
---|
1573 | }, |
---|
1574 | |
---|
1575 | runDefQ = function(referenceModule, mids){ |
---|
1576 | // defQ is an array of [id, dependencies, factory] |
---|
1577 | // mids (if any) is a vector of mids given by a combo service |
---|
1578 | var definedModules = [], |
---|
1579 | module, args; |
---|
1580 | while(defQ.length){ |
---|
1581 | args = defQ.shift(); |
---|
1582 | mids && (args[0]= mids.shift()); |
---|
1583 | // explicit define indicates possible multiple modules in a single file; delay injecting dependencies until defQ fully |
---|
1584 | // processed since modules earlier in the queue depend on already-arrived modules that are later in the queue |
---|
1585 | // TODO: what if no args[0] and no referenceModule |
---|
1586 | module = (args[0] && getModule(args[0])) || referenceModule; |
---|
1587 | definedModules.push([module, args[1], args[2]]); |
---|
1588 | } |
---|
1589 | consumePendingCacheInsert(referenceModule); |
---|
1590 | forEach(definedModules, function(args){ |
---|
1591 | injectDependencies(defineModule.apply(null, args)); |
---|
1592 | }); |
---|
1593 | }; |
---|
1594 | } |
---|
1595 | |
---|
1596 | var timerId = 0, |
---|
1597 | clearTimer = noop, |
---|
1598 | startTimer = noop; |
---|
1599 | if(has("dojo-timeout-api")){ |
---|
1600 | // Timer machinery that monitors how long the loader is waiting and signals an error when the timer runs out. |
---|
1601 | clearTimer = function(){ |
---|
1602 | timerId && clearTimeout(timerId); |
---|
1603 | timerId = 0; |
---|
1604 | }; |
---|
1605 | |
---|
1606 | startTimer = function(){ |
---|
1607 | clearTimer(); |
---|
1608 | if(req.waitms){ |
---|
1609 | timerId = window.setTimeout(function(){ |
---|
1610 | clearTimer(); |
---|
1611 | signal(error, makeError("timeout", waiting)); |
---|
1612 | }, req.waitms); |
---|
1613 | } |
---|
1614 | }; |
---|
1615 | } |
---|
1616 | |
---|
1617 | if (has("dom")) { |
---|
1618 | // Test for IE's different way of signaling when scripts finish loading. Note that according to |
---|
1619 | // http://bugs.dojotoolkit.org/ticket/15096#comment:14, IE9 also needs to follow the |
---|
1620 | // IE specific code path even though it has an addEventListener() method. |
---|
1621 | // Unknown if special path needed on IE10+, which also has a document.attachEvent() method. |
---|
1622 | // Should evaluate to false for Opera and Windows 8 apps, even though they document.attachEvent() |
---|
1623 | // is defined in both those environments. |
---|
1624 | has.add("ie-event-behavior", doc.attachEvent && typeof Windows === "undefined" && |
---|
1625 | (typeof opera === "undefined" || opera.toString() != "[object Opera]")); |
---|
1626 | } |
---|
1627 | |
---|
1628 | if(has("dom") && (has("dojo-inject-api") || has("dojo-dom-ready-api"))){ |
---|
1629 | var domOn = function(node, eventName, ieEventName, handler){ |
---|
1630 | // Add an event listener to a DOM node using the API appropriate for the current browser; |
---|
1631 | // return a function that will disconnect the listener. |
---|
1632 | if(!has("ie-event-behavior")){ |
---|
1633 | node.addEventListener(eventName, handler, false); |
---|
1634 | return function(){ |
---|
1635 | node.removeEventListener(eventName, handler, false); |
---|
1636 | }; |
---|
1637 | }else{ |
---|
1638 | node.attachEvent(ieEventName, handler); |
---|
1639 | return function(){ |
---|
1640 | node.detachEvent(ieEventName, handler); |
---|
1641 | }; |
---|
1642 | } |
---|
1643 | }, |
---|
1644 | windowOnLoadListener = domOn(window, "load", "onload", function(){ |
---|
1645 | req.pageLoaded = 1; |
---|
1646 | doc.readyState!="complete" && (doc.readyState = "complete"); |
---|
1647 | windowOnLoadListener(); |
---|
1648 | }); |
---|
1649 | |
---|
1650 | if(has("dojo-inject-api")){ |
---|
1651 | // if the loader is on the page, there must be at least one script element |
---|
1652 | // getting its parent and then doing insertBefore solves the "Operation Aborted" |
---|
1653 | // error in IE from appending to a node that isn't properly closed; see |
---|
1654 | // dojo/tests/_base/loader/requirejs/simple-badbase.html for an example |
---|
1655 | // don't use scripts with type dojo/... since these may be removed; see #15809 |
---|
1656 | // prefer to use the insertPoint computed during the config sniff in case a script is removed; see #16958 |
---|
1657 | var scripts = doc.getElementsByTagName("script"), |
---|
1658 | i = 0, |
---|
1659 | script; |
---|
1660 | while(!insertPointSibling){ |
---|
1661 | if(!/^dojo/.test((script = scripts[i++]) && script.type)){ |
---|
1662 | insertPointSibling= script; |
---|
1663 | } |
---|
1664 | } |
---|
1665 | |
---|
1666 | req.injectUrl = function(url, callback, owner){ |
---|
1667 | // insert a script element to the insert-point element with src=url; |
---|
1668 | // apply callback upon detecting the script has loaded. |
---|
1669 | |
---|
1670 | var node = owner.node = doc.createElement("script"), |
---|
1671 | onLoad = function(e){ |
---|
1672 | e = e || window.event; |
---|
1673 | var node = e.target || e.srcElement; |
---|
1674 | if(e.type === "load" || /complete|loaded/.test(node.readyState)){ |
---|
1675 | loadDisconnector(); |
---|
1676 | errorDisconnector(); |
---|
1677 | callback && callback(); |
---|
1678 | } |
---|
1679 | }, |
---|
1680 | loadDisconnector = domOn(node, "load", "onreadystatechange", onLoad), |
---|
1681 | errorDisconnector = domOn(node, "error", "onerror", function(e){ |
---|
1682 | loadDisconnector(); |
---|
1683 | errorDisconnector(); |
---|
1684 | signal(error, makeError("scriptError", [url, e])); |
---|
1685 | }); |
---|
1686 | |
---|
1687 | node.type = "text/javascript"; |
---|
1688 | node.charset = "utf-8"; |
---|
1689 | node.src = url; |
---|
1690 | insertPointSibling.parentNode.insertBefore(node, insertPointSibling); |
---|
1691 | return node; |
---|
1692 | }; |
---|
1693 | } |
---|
1694 | } |
---|
1695 | |
---|
1696 | if(has("dojo-log-api")){ |
---|
1697 | req.log = function(){ |
---|
1698 | try{ |
---|
1699 | for(var i = 0; i < arguments.length; i++){ |
---|
1700 | console.log(arguments[i]); |
---|
1701 | } |
---|
1702 | }catch(e){} |
---|
1703 | }; |
---|
1704 | }else{ |
---|
1705 | req.log = noop; |
---|
1706 | } |
---|
1707 | |
---|
1708 | if(has("dojo-trace-api")){ |
---|
1709 | var trace = req.trace = function( |
---|
1710 | group, // the trace group to which this application belongs |
---|
1711 | args // the contents of the trace |
---|
1712 | ){ |
---|
1713 | /// |
---|
1714 | // Tracing interface by group. |
---|
1715 | // |
---|
1716 | // Sends the contents of args to the console iff (req.trace.on && req.trace[group]) |
---|
1717 | |
---|
1718 | if(trace.on && trace.group[group]){ |
---|
1719 | signal("trace", [group, args]); |
---|
1720 | for(var arg, dump = [], text= "trace:" + group + (args.length ? (":" + args[0]) : ""), i= 1; i<args.length;){ |
---|
1721 | arg = args[i++]; |
---|
1722 | if(isString(arg)){ |
---|
1723 | text += ", " + arg; |
---|
1724 | }else{ |
---|
1725 | dump.push(arg); |
---|
1726 | } |
---|
1727 | } |
---|
1728 | req.log(text); |
---|
1729 | dump.length && dump.push("."); |
---|
1730 | req.log.apply(req, dump); |
---|
1731 | } |
---|
1732 | }; |
---|
1733 | mix(trace, { |
---|
1734 | on:1, |
---|
1735 | group:{}, |
---|
1736 | set:function(group, value){ |
---|
1737 | if(isString(group)){ |
---|
1738 | trace.group[group]= value; |
---|
1739 | }else{ |
---|
1740 | mix(trace.group, group); |
---|
1741 | } |
---|
1742 | } |
---|
1743 | }); |
---|
1744 | trace.set(mix(mix(mix({}, defaultConfig.trace), userConfig.trace), dojoSniffConfig.trace)); |
---|
1745 | on("config", function(config){ |
---|
1746 | config.trace && trace.set(config.trace); |
---|
1747 | }); |
---|
1748 | }else{ |
---|
1749 | req.trace = noop; |
---|
1750 | } |
---|
1751 | |
---|
1752 | var def = function( |
---|
1753 | mid, //(commonjs.moduleId, optional) |
---|
1754 | dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before running factory |
---|
1755 | factory //(any) |
---|
1756 | ){ |
---|
1757 | /// |
---|
1758 | // Advises the loader of a module factory. //Implements http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition. |
---|
1759 | /// |
---|
1760 | //note |
---|
1761 | // CommonJS factory scan courtesy of http://requirejs.org |
---|
1762 | |
---|
1763 | var arity = arguments.length, |
---|
1764 | defaultDeps = ["require", "exports", "module"], |
---|
1765 | // the predominate signature... |
---|
1766 | args = [0, mid, dependencies]; |
---|
1767 | if(arity==1){ |
---|
1768 | args = [0, (isFunction(mid) ? defaultDeps : []), mid]; |
---|
1769 | }else if(arity==2 && isString(mid)){ |
---|
1770 | args = [mid, (isFunction(dependencies) ? defaultDeps : []), dependencies]; |
---|
1771 | }else if(arity==3){ |
---|
1772 | args = [mid, dependencies, factory]; |
---|
1773 | } |
---|
1774 | |
---|
1775 | if(has("dojo-amd-factory-scan") && args[1]===defaultDeps){ |
---|
1776 | args[2].toString() |
---|
1777 | .replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg, "") |
---|
1778 | .replace(/require\(["']([\w\!\-_\.\/]+)["']\)/g, function(match, dep){ |
---|
1779 | args[1].push(dep); |
---|
1780 | }); |
---|
1781 | } |
---|
1782 | |
---|
1783 | req.trace("loader-define", args.slice(0, 2)); |
---|
1784 | var targetModule = args[0] && getModule(args[0]), |
---|
1785 | module; |
---|
1786 | if(targetModule && !waiting[targetModule.mid]){ |
---|
1787 | // given a mid that hasn't been requested; therefore, defined through means other than injecting |
---|
1788 | // consequent to a require() or define() application; examples include defining modules on-the-fly |
---|
1789 | // due to some code path or including a module in a script element. In any case, |
---|
1790 | // there is no callback waiting to finish processing and nothing to trigger the defQ and the |
---|
1791 | // dependencies are never requested; therefore, do it here. |
---|
1792 | injectDependencies(defineModule(targetModule, args[1], args[2])); |
---|
1793 | }else if(!has("ie-event-behavior") || !has("host-browser") || injectingCachedModule){ |
---|
1794 | // not IE path: anonymous module and therefore must have been injected; therefore, onLoad will fire immediately |
---|
1795 | // after script finishes being evaluated and the defQ can be run from that callback to detect the module id |
---|
1796 | defQ.push(args); |
---|
1797 | }else{ |
---|
1798 | // IE path: possibly anonymous module and therefore injected; therefore, cannot depend on 1-to-1, |
---|
1799 | // in-order exec of onLoad with script eval (since it's IE) and must manually detect here |
---|
1800 | targetModule = targetModule || injectingModule; |
---|
1801 | if(!targetModule){ |
---|
1802 | for(mid in waiting){ |
---|
1803 | module = modules[mid]; |
---|
1804 | if(module && module.node && module.node.readyState === 'interactive'){ |
---|
1805 | targetModule = module; |
---|
1806 | break; |
---|
1807 | } |
---|
1808 | } |
---|
1809 | if(has("dojo-combo-api") && !targetModule){ |
---|
1810 | for(var i = 0; i<combosPending.length; i++){ |
---|
1811 | targetModule = combosPending[i]; |
---|
1812 | if(targetModule.node && targetModule.node.readyState === 'interactive'){ |
---|
1813 | break; |
---|
1814 | } |
---|
1815 | targetModule= 0; |
---|
1816 | } |
---|
1817 | } |
---|
1818 | } |
---|
1819 | if(has("dojo-combo-api") && isArray(targetModule)){ |
---|
1820 | injectDependencies(defineModule(getModule(targetModule.shift()), args[1], args[2])); |
---|
1821 | if(!targetModule.length){ |
---|
1822 | combosPending.splice(i, 1); |
---|
1823 | } |
---|
1824 | }else if(targetModule){ |
---|
1825 | consumePendingCacheInsert(targetModule); |
---|
1826 | injectDependencies(defineModule(targetModule, args[1], args[2])); |
---|
1827 | }else{ |
---|
1828 | signal(error, makeError("ieDefineFailed", args[0])); |
---|
1829 | } |
---|
1830 | checkComplete(); |
---|
1831 | } |
---|
1832 | }; |
---|
1833 | def.amd = { |
---|
1834 | vendor:"dojotoolkit.org" |
---|
1835 | }; |
---|
1836 | |
---|
1837 | if(has("dojo-requirejs-api")){ |
---|
1838 | req.def = def; |
---|
1839 | } |
---|
1840 | |
---|
1841 | // allow config to override default implementation of named functions; this is useful for |
---|
1842 | // non-browser environments, e.g., overriding injectUrl, getText, log, etc. in node.js, Rhino, etc. |
---|
1843 | // also useful for testing and monkey patching loader |
---|
1844 | mix(mix(req, defaultConfig.loaderPatch), userConfig.loaderPatch); |
---|
1845 | |
---|
1846 | // now that req is fully initialized and won't change, we can hook it up to the error signal |
---|
1847 | on(error, function(arg){ |
---|
1848 | try{ |
---|
1849 | console.error(arg); |
---|
1850 | if(arg instanceof Error){ |
---|
1851 | for(var p in arg){ |
---|
1852 | console.log(p + ":", arg[p]); |
---|
1853 | } |
---|
1854 | console.log("."); |
---|
1855 | } |
---|
1856 | }catch(e){} |
---|
1857 | }); |
---|
1858 | |
---|
1859 | // always publish these |
---|
1860 | mix(req, { |
---|
1861 | uid:uid, |
---|
1862 | cache:cache, |
---|
1863 | packs:packs |
---|
1864 | }); |
---|
1865 | |
---|
1866 | |
---|
1867 | if(has("dojo-publish-privates")){ |
---|
1868 | mix(req, { |
---|
1869 | // these may be interesting to look at when debugging |
---|
1870 | paths:paths, |
---|
1871 | aliases:aliases, |
---|
1872 | modules:modules, |
---|
1873 | legacyMode:legacyMode, |
---|
1874 | execQ:execQ, |
---|
1875 | defQ:defQ, |
---|
1876 | waiting:waiting, |
---|
1877 | |
---|
1878 | // these are used for testing |
---|
1879 | // TODO: move testing infrastructure to a different has feature |
---|
1880 | packs:packs, |
---|
1881 | mapProgs:mapProgs, |
---|
1882 | pathsMapProg:pathsMapProg, |
---|
1883 | listenerQueues:listenerQueues, |
---|
1884 | |
---|
1885 | // these are used by the builder (at least) |
---|
1886 | computeMapProg:computeMapProg, |
---|
1887 | computeAliases:computeAliases, |
---|
1888 | runMapProg:runMapProg, |
---|
1889 | compactPath:compactPath, |
---|
1890 | getModuleInfo:getModuleInfo_ |
---|
1891 | }); |
---|
1892 | } |
---|
1893 | |
---|
1894 | // the loader can be defined exactly once; look for global define which is the symbol AMD loaders are |
---|
1895 | // *required* to define (as opposed to require, which is optional) |
---|
1896 | if(global.define){ |
---|
1897 | if(has("dojo-log-api")){ |
---|
1898 | signal(error, makeError("defineAlreadyDefined", 0)); |
---|
1899 | } |
---|
1900 | return; |
---|
1901 | }else{ |
---|
1902 | global.define = def; |
---|
1903 | global.require = req; |
---|
1904 | if(has("host-node")){ |
---|
1905 | require = req; |
---|
1906 | } |
---|
1907 | } |
---|
1908 | |
---|
1909 | if(has("dojo-combo-api") && req.combo && req.combo.plugins){ |
---|
1910 | var plugins = req.combo.plugins, |
---|
1911 | pluginName; |
---|
1912 | for(pluginName in plugins){ |
---|
1913 | mix(mix(getModule(pluginName), plugins[pluginName]), {isCombo:1, executed:"executed", load:1}); |
---|
1914 | } |
---|
1915 | } |
---|
1916 | |
---|
1917 | if(has("dojo-config-api")){ |
---|
1918 | forEach(delayedModuleConfig, function(c){ config(c); }); |
---|
1919 | var bootDeps = dojoSniffConfig.deps || userConfig.deps || defaultConfig.deps, |
---|
1920 | bootCallback = dojoSniffConfig.callback || userConfig.callback || defaultConfig.callback; |
---|
1921 | req.boot = (bootDeps || bootCallback) ? [bootDeps || [], bootCallback] : 0; |
---|
1922 | } |
---|
1923 | if(!has("dojo-built")){ |
---|
1924 | !req.async && req(["dojo"]); |
---|
1925 | req.boot && req.apply(null, req.boot); |
---|
1926 | } |
---|
1927 | }) |
---|
1928 | //>>excludeStart("replaceLoaderConfig", kwArgs.replaceLoaderConfig); |
---|
1929 | ( |
---|
1930 | // userConfig |
---|
1931 | (function(){ |
---|
1932 | // make sure we're looking at global dojoConfig etc. |
---|
1933 | return this.dojoConfig || this.djConfig || this.require || {}; |
---|
1934 | })(), |
---|
1935 | |
---|
1936 | // defaultConfig |
---|
1937 | { |
---|
1938 | // the default configuration for a browser; this will be modified by other environments |
---|
1939 | hasCache:{ |
---|
1940 | "host-browser":1, |
---|
1941 | "dom":1, |
---|
1942 | "dojo-amd-factory-scan":1, |
---|
1943 | "dojo-loader":1, |
---|
1944 | "dojo-has-api":1, |
---|
1945 | "dojo-inject-api":1, |
---|
1946 | "dojo-timeout-api":1, |
---|
1947 | "dojo-trace-api":1, |
---|
1948 | "dojo-log-api":1, |
---|
1949 | "dojo-dom-ready-api":1, |
---|
1950 | "dojo-publish-privates":1, |
---|
1951 | "dojo-config-api":1, |
---|
1952 | "dojo-sniff":1, |
---|
1953 | "dojo-sync-loader":1, |
---|
1954 | "dojo-test-sniff":1, |
---|
1955 | "config-deferredInstrumentation":1, |
---|
1956 | "config-useDeferredInstrumentation":"report-unhandled-rejections", |
---|
1957 | "config-tlmSiblingOfDojo":1 |
---|
1958 | }, |
---|
1959 | packages:[{ |
---|
1960 | // note: like v1.6-, this bootstrap computes baseUrl to be the dojo directory |
---|
1961 | name:'dojo', |
---|
1962 | location:'.' |
---|
1963 | },{ |
---|
1964 | name:'tests', |
---|
1965 | location:'./tests' |
---|
1966 | },{ |
---|
1967 | name:'dijit', |
---|
1968 | location:'../dijit' |
---|
1969 | },{ |
---|
1970 | name:'build', |
---|
1971 | location:'../util/build' |
---|
1972 | },{ |
---|
1973 | name:'doh', |
---|
1974 | location:'../util/doh' |
---|
1975 | },{ |
---|
1976 | name:'dojox', |
---|
1977 | location:'../dojox' |
---|
1978 | },{ |
---|
1979 | name:'demos', |
---|
1980 | location:'../demos' |
---|
1981 | }], |
---|
1982 | trace:{ |
---|
1983 | // these are listed so it's simple to turn them on/off while debugging loading |
---|
1984 | "loader-inject":0, |
---|
1985 | "loader-define":0, |
---|
1986 | "loader-exec-module":0, |
---|
1987 | "loader-run-factory":0, |
---|
1988 | "loader-finish-exec":0, |
---|
1989 | "loader-define-module":0, |
---|
1990 | "loader-circular-dependency":0, |
---|
1991 | "loader-define-nonmodule":0 |
---|
1992 | }, |
---|
1993 | async:0, |
---|
1994 | waitSeconds:15 |
---|
1995 | } |
---|
1996 | ); |
---|
1997 | //>>excludeEnd("replaceLoaderConfig") |
---|