-
Notifications
You must be signed in to change notification settings - Fork 0
/
dojo.js
1880 lines (1697 loc) · 62 KB
/
dojo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(function(
userConfig,
defaultConfig
){
// summary:
// This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with
// any AMD-compliant loader via the package main module dojo/main.
// description:
// This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured
// to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded
// IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package
// via the package main module dojo/main and this loader is not required; see dojo/package.json for details.
//
// In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables
// the dojo.provide/dojo.require etc. API. This machinery is loaded by default, but may be dynamically removed
// via the has.js API and statically removed via the build system.
//
// This loader includes sniffing machinery to determine the environment; the following environments are supported:
//
// * browser
// * node.js
// * rhino
//
// This is the so-called "source loader". As such, it includes many optional features that may be discarded by
// building a customized verion with the build system.
// Design and Implementation Notes
//
// This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC.
//
// This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)
// loader that can be configured to operate in either synchronous or asynchronous modes.
//
// Since this machinery implements a loader, it does not have the luxury of using a load system and/or
// leveraging a utility library. This results in an unpleasantly long file; here is a roadmap of the contents:
//
// 1. Small library for use implementing the loader.
// 2. Define the has.js API; this is used throughout the loader to bracket features.
// 3. Define the node.js and rhino sniffs and sniff.
// 4. Define the loader's data.
// 5. Define the configuration machinery.
// 6. Define the script element sniffing machinery and sniff for configuration data.
// 7. Configure the loader IAW the provided user, default, and sniffing data.
// 8. Define the global require function.
// 9. Define the module resolution machinery.
// 10. Define the module and plugin module definition machinery
// 11. Define the script injection machinery.
// 12. Define the DOMContentLoad detection and ready API.
// 13. Define the logging API.
// 14. Define the tracing API.
// 15. Define the error API.
// 16. Define the AMD define function.
// 17. Define the dojo v1.x provide/require machinery.
// 18. Publish global variables.
//
// Language and Acronyms and Idioms
//
// moduleId: a CJS module identifier, (used for public APIs)
// mid: moduleId (used internally)
// packageId: a package identifier (used for public APIs)
// pid: packageId (used internally); the implied system or default package has pid===""
// package-qualified name: a mid qualified by the pid of which the module is a member; result is the string pid + "*" + mid
// pqn: package-qualified name
// pack: package is used internally to reference a package object (since javascript has reserved words including "package")
// The integer constant 1 is used in place of true and 0 in place of false.
var
// define a minimal library to help build the loader
noop = function(){
},
isEmpty = function(it){
for(var p in it){
return 0;
}
return 1;
},
toString = {}.toString,
isFunction = function(it){
return toString.call(it) == "[object Function]";
},
isString = function(it){
return toString.call(it) == "[object String]";
},
isArray = function(it){
return toString.call(it) == "[object Array]";
},
forEach = function(vector, callback){
if(vector){
for(var i = 0; i < vector.length;){
callback(vector[i++]);
}
}
},
setIns = function(set, name){
set[name] = 1;
},
setDel = function(set, name){
delete set[name];
},
mix = function(dest, src){
for(var p in src){
dest[p] = src[p];
}
return dest;
},
escapeRegEx = function(s){
return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){
return "\\" + c;
});
},
uidSeed = 1,
uid = function(){
// Returns a unique indentifier (within the lifetime of the document) of the form /_d+/.
return "_" + uidSeed++;
},
// FIXME: how to doc window.require() api
// this will be the global require function; define it immediately so we can start hanging things off of it
req = function(
config, //(object, optional) hash of configuration properties
dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback
callback //(function, optional) lamda expression to apply to module values implied by dependencies
){
return contextRequire(config, dependencies, callback, 0, req);
},
// the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout
global = this,
doc = global.document,
element = doc && doc.createElement("DiV"),
has = req.has = function(name){
return hasCache[name] = isFunction(hasCache[name]) ? hasCache[name](global, doc, element) : hasCache[name];
},
hasCache = has.cache = defaultConfig.hasCache;
has.add = function(name, test, now, force){
(hasCache[name]===undefined || force) && (hasCache[name] = test);
return now && has(name);
};
has.add("host-node", typeof process == "object" && /\/node/.test(process.execPath));
if(has("host-node")){
// fixup the default config for node.js environment
require("./_base/configNode.js").config(defaultConfig);
// remember node's require (with respect to baseUrl==dojo's root)
defaultConfig.nodeRequire = require;
// recompute userConfig because passed userConfig argument was wrong in node
userConfig = global.dojoConfig || global.djConfig || global.require || {};
}
has.add("host-rhino", typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"));
if(has("host-rhino")){
// owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl...
for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){
arg = (rhinoArgs[i++] + "").split("=");
if(arg[0] == "baseUrl"){
baseUrl = arg[1];
break;
}
}
load(baseUrl + "/_base/configRhino.js");
rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs);
}
// userConfig has tests override defaultConfig has tests; do this after the environment detection because
// the environment detection usually sets some has feature values in the hasCache.
for(var p in userConfig.has){
has.add(p, userConfig.has[p], 0, 1);
}
//
// define the loader data
//
// the loader will use these like symbols if the loader has the traceApi; otherwise
// define magic numbers so that modules can be provided as part of defaultConfig
var
requested = 1,
arrived = 2,
nonmodule = 3,
executing = 4,
executed = 5,
execThrew = 6;
if(has("dojo-trace-api")){
// TODO: consider moving the symbol table to a public API offered by the loader
var
symbols =
{},
symbol = function(name){
return symbols[name] || (symbols[name] = {value:name});
};
// these make debugging nice
requested = symbol("requested");
arrived = symbol("arrived");
nonmodule = symbol("not-a-module");
executing = symbol("executing");
executed = symbol("executed");
execThrew = symbol("exec-threw");
}
if(has("dojo-combo-api")){
req.combo = {add:noop};
var
comboPending = 0,
combosPending = [];
}
// lexical variables that hold key loader data structures; may be completely initialized by
// defaultConfig for optimized/built versions of the loader.
mix(req, defaultConfig);
delete req.packages;
var reqEval, paths, pathsMapProg, packs, packageMap, packageMapProg, modules, cache, cacheBust;
if(has("dojo-auto-init")){
paths = req.paths;
pathsMapProg = req.pathsMapProg;
packs = req.packs;
packageMap = req.packageMap;
packageMapProg = req.packageMapProg;
modules = req.modules;
cache = req.cache;
cacheBust = req.cacheBust;
}else{
// CommonJS paths
paths = {};
pathsMapProg = [];
// list of (from-path, to-path, regex, length) derived from paths;
// a "program" to apply paths; see computeMapProg
packs = {};
// a map from packageId to package configuration object; see fixupPackageInfo
packageMap = {};
// map from package name to local-installed package name
packageMapProg = [];
// list of (from-package, to-package, regex, length) derived from packageMap;
// a "program" to apply paths; see computeMapProg
// A hash:(pqn) --> (module-object). module objects are simple JavaScript objects with the
// following properties:
//
// pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package
// id: the module identifier without the package identifier (e.g., "io/script")
// pqn: the full package-qualified name (e.g., "dojo*io/script")
// url: the URL from which the module was retrieved
// pack: the package object of the package to which the module belongs
// path: the full module name (package + path) resolved with respect to the loader (i.e., mappings have been applied) (e.g., dojo/io/script)
// executed: 0 => not executed; executing => in the process of tranversing deps and running factory; executed => factory has been executed
// deps: the dependency vector for this module (vector of modules objects)
// def: the factory for this module
// result: the result of the running the factory for this module
// injected: (requested | arrived | nonmodule) the status of the module; nonmodule means the resource did not call define
// load: plugin load function; applicable only for plugins
//
// Modules go through several phases in creation:
//
// 1. Requested: some other module's definition or a require application contained the requested module in
// its dependency vector or executing code explicitly demands a module via req.require.
//
// 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL
//
// 3. Loaded: the resource injected in [2] has been evalated.
//
// 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some
// resources may just contain a bundle of code and never formally define a module via define
//
// 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result.
modules = {};
///
// hash:(pqn)-->(function)
///
// Gives the contents of a cached resource; function should cause the same actions as if the given pqn was downloaded
// and evaluated by the host environment
cache = {}
cacheBust = "";
}
reqEval = req.eval ||
// use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution
new Function("__text", "__hint", 'return eval(__text + "\\r\\n////@ sourceURL=" + __hint);');
var listenerConnection= function(listener, queue){
queue.push(listener);
this.l = listener;
this.q = queue;
};
listenerConnection.prototype.remove= function(){
for(var queue = this.q, listener = this.l, i = 0; i<queue.length; i++){
if(queue[i]===listener){
queue.splice(i, 1);
return;
}
}
};
var
listenerQueues = {},
idleListeners = listenerQueues.idle= [],
signal = function(queue, args){
// notice we run a copy of the queue; this allows listeners to add/remove
// other listeners without affecting this particular signal
forEach(queue.slice(0), function(listener){
listener.apply(null, args);
});
};
req.on = function(type, listener){
// notice that connecting to a nonexisting type just results in a connection that will never
// get signaled yet still has a valid remove method. This allows client code to make connections
// to queues that may or may not exist (say, depending on config or build options) and later call
// remove safely.
return new listenerConnection(listener, listenerQueues[type] || []);
};
//
// configuration machinery (with an optimized/built defaultConfig, this can be discarded)
//
if(has("dojo-config-api")){
var
configListeners = listenerQueues.config = [],
// vector of registered listener functions for config changes
computeMapProg = function(map, dest){
// This routine takes a map target-prefix(string)-->replacement(string) into a vector
// of quads (target-prefix, replacement, regex-for-target-prefix, length-of-target-prefix)
//
// The loader contains processes that map one string prefix to another. These
// are encountered when applying the requirejs paths configuration and when mapping
// package names. We can make the mapping and any replacement easier and faster by
// replacing the map with a vector of quads and then using this structure in the simple machine runMapProg.
dest.splice(0, dest.length);
var p, i, item;
for(p in map){
dest.push([p, map[p]]);
}
dest.sort(function(lhs, rhs){
return rhs[0].length - lhs[0].length;
});
for(i = 0; i < dest.length;){
item = dest[i++];
item[2] = new RegExp("^" + escapeRegEx(item[0]) + "(\/|$)");
item[3] = item[0].length + 1;
}
},
fixupPackageInfo = function(packageInfo, baseUrl){
// calculate the precise (name, baseUrl, main, mappings) for a package
var name = packageInfo.name;
if(!name){
// packageInfo must be a string that gives the name
name = packageInfo;
packageInfo = {name:name};
}
packageInfo = mix({main:"main", mapProg:[]}, packageInfo);
packageInfo.location = (baseUrl || "") + (packageInfo.location ? packageInfo.location : name);
computeMapProg(packageInfo.packageMap, packageInfo.mapProg);
if(!packageInfo.main.indexOf("./")){
packageInfo.main = packageInfo.main.substring(2);
}
// allow paths to be specified in the package info
mix(paths, packageInfo.paths);
// now that we've got a fully-resolved package object, push it into the configuration
packs[name] = packageInfo;
packageMap[name] = name;
},
configVariableNames = { async:1, xd:1, waitSeconds:1, cacheBust:1, baseUrl:1, locale:1, combo:1 },
config = function(config, booting){
// mix config into require
var p;
// async, cacheBust, and baseUrl just replace whatever is already there
// async is only meaningful if it's set before booting the loader
for(p in config){
if(configVariableNames[p]){
req[p] = config[p];
}
if(config[p]!==hasCache){
// accumulate raw config info for client apps which can use this to pass their own config
req.rawConfig[p] = config[p];
has.add("config-"+p, config[p], 0, booting);
}
}
req.waitms = (req.waitSeconds || 0) * 1000;
// TODO: why do we need the toString and replace...can't the config be assumed correct?
cacheBust = ((req.cacheBust || "")+"").replace(/\W+/g,"");
// now do the special work for has, packagePaths, packages, paths, deps, callback, and ready
for(p in config.has){
has.add(p, config.has[p], 0, booting);
}
// make sure baseUrl ends with a slash
if(!req.baseUrl){
req.baseUrl = "./";
}else if(!/\/$/.test(req.baseUrl)){
req.baseUrl += "/";
}
// for each package found in any packages config item, augment the packs map owned by the loader
forEach(config.packages, fixupPackageInfo);
// for each packagePath found in any packagePaths config item, augment the packs map owned by the loader
for(baseUrl in config.packagePaths){
forEach(config.packagePaths[baseUrl], function(packageInfo){
fixupPackageInfo(packageInfo, baseUrl + "/");
});
}
// backcompat
config.modulePaths && !config.paths && (config.paths= config.modulePaths);
// push in any paths and recompute the internal pathmap
// warning: this cann't be done until the package config is processed since packages may include path info
computeMapProg(mix(paths, config.paths), pathsMapProg);
// mix any packageMap config item and recompute the internal packageMapProg
computeMapProg(mix(packageMap, config.packageMap), packageMapProg);
// push in any new cache values
mix(cache, config.cache);
(function(deps, callback){
var args = ((deps && deps.length) || callback) && [deps || [], callback || noop];
if(booting){
args && (req.bootRequire= args);
}else{
args && req(args[0], args[1]);
}
})(config.deps, config.callback);
signal(configListeners, [config, req.rawConfig]);
};
//
// execute the various sniffs
//
var dojoSniffConfig = {};
if(has("dojo-sniff")){
for(var src, match, scripts = doc.getElementsByTagName("script"), i = 0; i < scripts.length && !match; i++){
if((src = scripts[i].getAttribute("src")) && (match = src.match(/(.*)\/?dojo\.js(\W|$)/i))){
// if baseUrl wasn't explicitly set, set it here to the dojo directory; this is the 1.6- behavior
userConfig.baseUrl = userConfig.baseUrl || defaultConfig.baseUrl || match[1];
// see if there's a dojo configuration stuffed into the node
src = (scripts[i].getAttribute("data-dojo-config") || scripts[i].getAttribute("djConfig"));
if(src){
dojoSniffConfig = reqEval("({ " + src + " })", "data-dojo-config");
}
if(has("dojo-requirejs-api")){
var dataMain = scripts[i].getAttribute("data-main");
if(dataMain){
dojoSniffConfig.deps = dojoSniffConfig.deps || [dataMain];
}
}
}
}
}
if(has("dojo-test-sniff")){
// pass down doh.testConfig from parent as if it were a data-dojo-config
try{
if(window.parent != window && window.parent.require){
var doh = window.parent.require("doh");
doh && mix(dojoSniffConfig, doh.testConfig);
}
}catch(e){}
}
// configure the loader; let the user override defaults
req.rawConfig = {};
config(defaultConfig, 1);
config(userConfig, 1);
config(dojoSniffConfig, 1);
}
//
// build the loader machinery iaw configuration, including has feature tests
//
var
injectDependencies = function(module){
forEach(module.deps, injectModule);
if(has("dojo-combo-api") && comboPending){
comboPending = 0;
req.combo.done(function(mids, url) {
var onLoadCallback= function(){
// defQ is a vector of module definitions 1-to-1, onto mids
runDefQ(0, mids);
checkComplete();
};
combosPending.push(mids);
injectingModule = mids;
req.injectUrl(url, onLoadCallback, mids);
injectingModule = 0;
}, req);
}
},
contextRequire = function(a1, a2, a3, referenceModule, contextRequire){
var module, syntheticMid;
if(isString(a1)){
// signature is (moduleId)
module = getModule(a1, referenceModule, 1);
if(module.plugin){
injectPlugin(module, true);
}
return module.result;
}
if(!isArray(a1)){
// a1 is a configuration
config(a1);
// juggle args; (a2, a3) may be (dependencies, callback)
a1 = a2;
a2 = a3;
}
if(isArray(a1)){
// signature is (requestList [,callback])
// resolve the request list with respect to the reference module
for(var deps = [], i = 0; i < a1.length;){
deps.push(getModule(a1[i++], referenceModule, 1));
}
// construct a synthetic module to control execution of the requestList, and, optionally, callback
syntheticMid = uid();
module = mix(makeModuleInfo("", syntheticMid, "*" + syntheticMid, 0, "", ""), {
injected: arrived,
deps: deps,
def: a2 || noop
});
modules[module.pqn] = module;
injectDependencies(module);
// try to immediately execute
checkCompleteGuard++;
if(execModule(module, 1) === abortExec){
// some deps weren't on board; therefore, push into the execQ
execQ.push(module);
}
checkCompleteGuard--;
}
return contextRequire;
},
createRequire = function(module){
var result = module.require;
if(!result){
result = function(a1, a2, a3){
return contextRequire(a1, a2, a3, module, result);
};
module.require = mix(result, req);
result.toUrl = function(name){
return toUrl(name, module);
};
result.toAbsMid = function(mid){
// FIXME: the .path is wrong for a package main module
return getModuleInfo(mid, module, packs, modules, req.baseUrl, packageMapProg, pathsMapProg).path;
};
if(has("dojo-undef-api")){
result.undef = function(moduleId){
// In order to reload a module, it must be undefined (this routine) and then re-requested.
// This is useful for testing frameworks (at least).
var pqn = getModule(moduleId, module).pqn;
setDel(modules, pqn);
setDel(waiting, pqn);
};
}
}
return result;
},
xdomainAlways = 1,
xdomainWhenRequired = 2,
xdomain =
req.async=="xd" ? xdomainAlways : 0,
syncDepth =
///
// syncDepth==0 iff async AMD mode; otherwise either synchronous or xdomain mode; if > 1, then syncDepth-1 gives
// the recursive depth while loading a resource
req.async && !xdomain ? 0 : 1,
syncLoadComplete =
syncDepth,
execQ =
///
// The list of modules that need to be evaluated.
[],
defQ =
// The queue of define arguments sent to loader.
[],
waiting =
// The set of modules upon which the loader is waiting for definition to arrive
{},
execComplete = req.idle =
// says the loader has completed (or not) its work
function(){
return syncDepth == syncLoadComplete && !defQ.length && isEmpty(waiting) && !execQ.length;
},
runMapProg = function(targetMid, map){
// search for targetMid in map; return the map item if found; falsy otherwise
for(var i = 0; i < map.length; i++){
if(map[i][2].test(targetMid)){
return map[i];
}
}
return 0;
},
compactPath = function(path){
var
result = [],
segment, lastSegment;
path = path.split("/");
while(path.length){
segment = path.shift();
if(segment==".." && result.length && lastSegment!=".."){
result.pop();
}else if(segment!="."){
result.push(lastSegment= segment);
} // else ignore "."
}
return result.join("/");
},
makeModuleInfo = function(pid, mid, pqn, pack, path, url){
return {pid:pid, mid:mid, pqn:pqn, pack:pack, path:path, url:url, executed:0, def:0};
},
getModuleInfo = function(mid, referenceModule, packs, modules, baseUrl, packageMapProg, pathsMapProg, alwaysCreate){
// arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder)
// alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader
var pid, pack, pqn, mapProg, mapItem, path, url, result, isRelative, requestedMid;
requestedMid = mid;
isRelative = /^\./.test(mid);
if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){
// absolute path or protocol, or relative path but no reference module and therefore relative to page
// whatever it is, it's not a module but just a URL of some sort
return makeModuleInfo(0, mid, "*" + mid, 0, mid, mid);
}else{
// relative module ids are relative to the referenceModule; get rid of any dots
path = compactPath(isRelative ? (referenceModule.path + "/../" + mid) : mid);
if(/^\./.test(path)){
// the path is irrational
throw new Error("The path " + path + " is irrational");
}
// find the package indicated by the path, if any
mapProg = referenceModule && referenceModule.pack && referenceModule.pack.mapProg;
mapItem = (mapProg && runMapProg(path, mapProg)) || runMapProg(path, packageMapProg);
if(mapItem){
// mid specified a module that's a member of a package; figure out the package id and module id
// notice we expect pack.main to be valid with no pre or post slash
pid = mapItem[1];
mid = path.substring(mapItem[3]);
pack = packs[pid];
if(!mid){
mid= pack.main;
}
path = pid + "/" + mid;
}else{
pid = "";
mid = path;
}
pqn = pid + "*" + mid;
result = modules[pqn];
if(result){
return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pqn, result.pack, result.path, result.url) : modules[pqn];
}
}
// get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the
// fully resolved (i.e., all relative indicators and package mapping resolved) module id
if(!url){
mapItem = runMapProg(path, pathsMapProg);
if(mapItem){
url = mapItem[1] + path.substring(mapItem[3] - 1);
}else if(pid){
url = pack.location + "/" + mid;
}else if(has("config-tlmSiblingOfDojo")){
url = "../" + path;
}else{
url = path;
}
// if result is not absolute, add baseUrl
if(!(/(^\/)|(\:)/.test(url))){
url = baseUrl + url;
}
url += ".js";
}
return makeModuleInfo(pid, mid, pqn, pack, path, compactPath(url));
},
getModule = function(mid, referenceModule, fromRequire){
// compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule
var match, plugin, pluginResource, result, pqn;
match = mid.match(/^(.+?)\!(.*)$/);
//TODO: change the regex above to this and test...match= mid.match(/^([^\!]+)\!(.+)$/);
if(match){
// name was <plugin-module>!<plugin-resource>
plugin = getModule(match[1], referenceModule);
plugin.isPlugin = 1;
pluginResource = match[2];
pqn = plugin.pqn + "!" + (referenceModule ? referenceModule.pqn + "!" : "") + pluginResource;
return modules[pqn] || (modules[pqn] = {plugin:plugin, mid:pluginResource, req:(referenceModule ? createRequire(referenceModule) : req), pqn:pqn});
}else{
if(fromRequire && /^.*[^\/\.]+\.[^\/\.]+$/.test(mid)){
// anything* anything-other-than-a-dot+ dot anything-other-than-a-dot-or-slash+ => a url that ends with a filetype
pqn = "*" + mid;
return modules[pqn]= modules[pqn] || makeModuleInfo(0, mid, pqn, 0, mid, mid);
}
result = getModuleInfo(mid, referenceModule, packs, modules, req.baseUrl, packageMapProg, pathsMapProg);
return modules[result.pqn] || (modules[result.pqn] = result);
}
},
toUrl = req.toUrl = function(name, referenceModule){
// name must include a filetype; fault tolerate to allow no filetype (but things like "path/to/version2.13" will assume filetype of ".13")
var
match = name.match(/(.+)(\.[^\/\.]+?)$/),
root = (match && match[1]) || name,
ext = (match && match[2]) || "",
moduleInfo = getModuleInfo(root, referenceModule, packs, modules, req.baseUrl, packageMapProg, pathsMapProg),
url= moduleInfo.url;
// recall, getModuleInfo always returns a url with a ".js" suffix iff pid; therefore, we've got to trim it
url= typeof moduleInfo.pid == "string" ? url.substring(0, url.length - 3) : url;
return url + ext;
},
nonModuleProps = {
injected: arrived,
deps: [],
def: nonmodule,
result: nonmodule,
executed: executed
},
cjsRequireModule = mix(getModule("require"), nonModuleProps),
cjsExportsModule = mix(getModule("exports"), nonModuleProps),
cjsModuleModule = mix(getModule("module"), nonModuleProps),
// this is a flag to say at least one factory was run during a deps tree traversal
runFactory = function(pqn, factory, args, cjs){
req.trace("loader-run-factory", [pqn]);
var result= isFunction(factory) ? factory.apply(null, args) : factory;
return result===undefined && cjs ? cjs.exports : result;
},
abortExec = {},
defOrder = 0,
finishExec = function(module){
module.executed = executed;
module.defOrder = defOrder++;
has("dojo-sync-loader") && module.finishProvides && module.finishProvides();
if(module.loadQ){
// this was a plugin module
var
q = module.loadQ,
load = module.load = module.result.load;
while(q.length){
load.apply(null, q.shift());
}
module.loadQ = 0;
}
// remove all occurences of this module from the execQ
for(i = 0; i < execQ.length;){
if(execQ[i] === module){
execQ.splice(i, 1);
}else{
i++;
}
}
req.trace("loader-exec-module", ["complete", module.pqn]);
},
execModule = function(module, strict){
// run the dependency vector, then run the factory for module
if(!module.executed){
if(!module.def || (strict && module.executed===executing)){
return abortExec;
}else if(module.executed===executing){
return module.result;
}
var pqn = module.pqn,
deps = module.deps || [],
arg, argResult,
args = [],
i = 0;
req.trace("loader-exec-module", ["exec", pqn]);
// for circular dependencies, assume the first module encountered was executed OK
// modules that circularly depend on a module that has not run its factory will get
// the premade cjs.exports===module.result. They can take a reference to this object and/or
// add properties to it. When the module finally runs its factory, the factory can
// read/write/replace this object. Notice that so long as the object isn't replaced, any
// reference taken earlier while walking the deps list is still valid.
module.executed = executing;
while(i < deps.length){
arg = deps[i++];
argResult = ((arg === cjsRequireModule) ? createRequire(module) :
((arg === cjsExportsModule) ? module.cjs.exports :
((arg === cjsModuleModule) ? module.cjs :
execModule(arg, strict))));
if(argResult === abortExec || (strict && arg.executed===executing)){
module.executed = 0;
req.trace("loader-exec-module", ["abort", pqn]);
return abortExec;
}
args.push(argResult);
}
if(has("dojo-loader-catches")){
try{
module.result = runFactory(pqn, module.def, args, module.cjs);
}catch(e){
module.executed = execThrew;
if(!req.error("loader/exec", [module, e, pqn].concat(args))){
throw e;
}
}
}else{
module.result = runFactory(pqn, module.def, args, module.cjs);
}
finishExec(module);
}
return module.result;
},
checkCompleteGuard = 0,
checkCompleteCalled,
checkComplete = function(){
// keep going through the execQ as long as at least one factory is executed
// plugins, recursion, cached modules all make for many execution path possibilities
if(checkCompleteGuard){
checkCompleteCalled++;
return;
}
checkCompleteGuard++;
isEmpty(waiting) && clearTimer();
var pluginsOnly= 0;
if(has("dojo-sync-loader") && syncDepth){
// synchronous loader in sync mode
if(syncDepth>1 || !isEmpty(waiting)){
// if we're into a recursive synchronous load and/or waiting for modules (this can only happen
// in xdomain mode) then only load plugins (we assume they are modern modules and can be
// executed out of order)
pluginsOnly= 1;
}
if(xdomain==xdomainWhenRequired && isEmpty(waiting)){
// we're coniditionally in xdomain mode because a module was requested that was
// xdomain, but now everything is on board, so shift back into not-xdomain-mode
xdomain = 0;
}
}
for(var currentDefOrder, module, i = 0; i < execQ.length;){
checkCompleteCalled = 0;
currentDefOrder = defOrder;
module = execQ[i];
(!pluginsOnly || module.isPlugin) && execModule(module);
if(checkCompleteCalled || currentDefOrder!=defOrder){
// defOrder was bumped one or more times indicating something was executed (note, this indicates
// the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute)
// checkcomplete was called while trying to execute the module (maybe a plugin loaded to plugin resources)
// either way, more modules may be able to execute earlier in the queue; therefore, restart
i = 0;
}else{
// nothing happend; check the next module in the exec queue
i++;
}
if(has("dojo-sync-loader") && syncDepth==1 && pluginsOnly && isEmpty(waiting)){
// we were still waiting for modules and therefore executed plugins only but after executing
// the plugins, there are no more modules to wait for, so now we can look at everything
pluginsOnly = i = 0;
}
}
checkCompleteGuard--;
if(execComplete()){
signal(idleListeners, []);
}
},
getXhr = 0;
// the dojo loader needs/optionally provides an XHR factory
if(has("dojo-sync-loader") || has("dojo-xhr-factory")){
has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:");
has.add("native-xhr", typeof XMLHttpRequest != "undefined");
if(has("native-xhr") && !has("dojo-force-activex-xhr")){
getXhr = function(){
return new XMLHttpRequest();
};
}else{
// if in the browser and old IE; find an xhr
for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){
try{
progid = XMLHTTP_PROGIDS[i++];
if(new ActiveXObject(progid)){
// this progid works; therefore, use it from now on
break;
}
}catch(e){
// squelch; we're just trying to find a good ActiveX progid
// if they all fail, then progid ends up as the last attempt and that will signal the error
// the first time the client actually tries to exec an xhr
}
}
getXhr = function(){
return new ActiveXObject(progid);
};
}
req.getXhr = getXhr;
}
// the dojo loader needs/optionally provides a getText API
if(has("dojo-sync-loader") || has("dojo-gettext-api")){
var getText = req.getText = req.getText || function(url, async, onLoad){
url = fixupUrl(url);
var xhr = getXhr();
if(async){
xhr.open('GET', url, true);
xhr.onreadystatechange = function(){
xhr.readyState == 4 && onLoad(xhr.responseText, async);
};
xhr.send(null);
return xhr;
}else{
xhr.open('GET', url, false);
xhr.send(null);
if(xhr.status == 200 || (!location.host && !xhr.status)){
if(onLoad){
onLoad(xhr.responseText, async);
}
}else{
throw new Error("XHR failed:" + xhr.status);
}
return xhr.responseText;
}
};
}
req.toAbsMid = function(id){
return id;
};
if(has("dojo-undef-api")){
req.undef = function(moduleId){
// In order to reload a module, it must be undefined (this routine) and then re-requested.
// This is useful for testing frameworks (at least).
var pqn = getModule(moduleId, 0).pqn;
setDel(modules, pqn);
setDel(waiting, pqn);
};
}