]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/_base/_loader/loader_xd.js
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
[tt-rss.git] / lib / dojo / _base / _loader / loader_xd.js
1 /*
2         Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3         Available via Academic Free License >= 2.1 OR the modified BSD license.
4         see: http://dojotoolkit.org/license for details
5 */
6
7
8 if(!dojo._hasResource["dojo._base._loader.loader_xd"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo._hasResource["dojo._base._loader.loader_xd"] = true;
10 //Cross-domain resource loader.
11 dojo.provide("dojo._base._loader.loader_xd");
12
13 dojo._xdReset = function(){
14         //summary: Internal xd loader function. Resets the xd state.
15
16         //This flag indicates where or not we have crossed into xdomain territory. Once any resource says
17         //it is cross domain, then the rest of the resources have to be treated as xdomain because we need
18         //to evaluate resources in order. If there is a xdomain resource followed by a xhr resource, we can't load
19         //the xhr resource until the one before it finishes loading. The text of the xhr resource will be converted
20         //to match the format for a xd resource and put in the xd load queue.
21         dojo._isXDomain = dojo.config.useXDomain || false;
22
23         dojo._xdClearInterval();
24         dojo._xdInFlight = {};
25         dojo._xdOrderedReqs = [];
26         dojo._xdDepMap = {};
27         dojo._xdContents = [];
28         dojo._xdDefList = [];
29 }
30
31 dojo._xdClearInterval = function(){
32         //summary: Internal xd loader function.
33         //Clears the interval timer used to check on the
34         //status of in-flight xd module resource requests.
35         if(dojo._xdTimer){
36                 clearInterval(dojo._xdTimer);
37                 dojo._xdTimer = 0;
38         }
39 }
40
41
42 //Call reset immediately to set the state.
43 dojo._xdReset();
44
45 dojo._xdCreateResource = function(/*String*/contents, /*String*/resourceName, /*String*/resourcePath){
46         //summary: Internal xd loader function. Creates an xd module source given an
47         //non-xd module contents.
48
49         //Remove comments. Not perfect, but good enough for dependency resolution.
50         var depContents = contents.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg , "");
51
52         //Find dependencies.
53         var deps = [];
54     var depRegExp = /dojo.(require|requireIf|provide|requireAfterIf|platformRequire|requireLocalization)\s*\(([\w\W]*?)\)/mg;
55     var match;
56         while((match = depRegExp.exec(depContents)) != null){
57                 if(match[1] == "requireLocalization"){
58                         //Need to load the local bundles asap, since they are not
59                         //part of the list of modules watched for loading.
60                         eval(match[0]);
61                 }else{
62                         deps.push('"' + match[1] + '", ' + match[2]);
63                 }
64         }
65
66         //Create resource object and the call to _xdResourceLoaded.
67         var output = [];
68         output.push(dojo._scopeName + "._xdResourceLoaded(function(" + dojo._scopePrefixArgs + "){\n");
69
70         //See if there are any dojo.loadInit calls
71         var loadInitCalls = dojo._xdExtractLoadInits(contents);
72         if(loadInitCalls){
73                 //Adjust fileContents since extractLoadInits removed something.
74                 contents = loadInitCalls[0];
75                 
76                 //Add any loadInit calls to the top of the xd file.
77                 for(var i = 1; i < loadInitCalls.length; i++){
78                         output.push(loadInitCalls[i] + ";\n");
79                 }
80         }
81
82         output.push("return {");
83
84         //Add dependencies
85         if(deps.length > 0){
86                 output.push("depends: [");
87                 for(i = 0; i < deps.length; i++){
88                         if(i > 0){
89                                 output.push(",\n");
90                         }
91                         output.push("[" + deps[i] + "]");
92                 }
93                 output.push("],");
94         }
95
96         //Add the contents of the file inside a function.
97         //Pass in scope arguments so we can support multiple versions of the
98         //same module on a page.
99         output.push("\ndefineResource: function(" + dojo._scopePrefixArgs + "){");
100
101         //Don't put in the contents in the debugAtAllCosts case
102         //since the contents may have syntax errors. Let those
103         //get pushed up when the script tags are added to the page
104         //in the debugAtAllCosts case.
105         if(!dojo.config["debugAtAllCosts"] || resourceName == "dojo._base._loader.loader_debug"){
106                 output.push(contents);
107         }
108         //Add isLocal property so we know if we have to do something different
109         //in debugAtAllCosts situations.
110         output.push("\n}, resourceName: '" + resourceName + "', resourcePath: '" + resourcePath + "'};});");
111         
112         return output.join(""); //String
113 }
114
115 dojo._xdExtractLoadInits = function(/*String*/fileContents){
116         //Extracts
117         var regexp = /dojo.loadInit\s*\(/g;
118         regexp.lastIndex = 0;
119
120         var parenRe = /[\(\)]/g;
121         parenRe.lastIndex = 0;
122
123         var results = [];
124         var matches;
125         while((matches = regexp.exec(fileContents))){
126                 //Find end of the call by finding the matching end paren
127                 parenRe.lastIndex = regexp.lastIndex;
128                 var matchCount = 1;
129                 var parenMatch;
130                 while((parenMatch = parenRe.exec(fileContents))){
131                         if(parenMatch[0] == ")"){
132                                 matchCount -= 1;
133                         }else{
134                                 matchCount += 1;
135                         }
136                         if(matchCount == 0){
137                                 break;
138                         }
139                 }
140                 
141                 if(matchCount != 0){
142                         throw "unmatched paren around character " + parenRe.lastIndex + " in: " + fileContents;
143                 }
144
145                 //Put the master matching string in the results.
146                 var startIndex = regexp.lastIndex - matches[0].length;
147                 results.push(fileContents.substring(startIndex, parenRe.lastIndex));
148
149                 //Remove the matching section.
150                 var remLength = parenRe.lastIndex - startIndex;
151                 fileContents = fileContents.substring(0, startIndex) + fileContents.substring(parenRe.lastIndex, fileContents.length);
152
153                 //Move the master regexp past the last matching paren point.
154                 regexp.lastIndex = parenRe.lastIndex - remLength;
155
156                 regexp.lastIndex = parenRe.lastIndex;
157         }
158
159         if(results.length > 0){
160                 results.unshift(fileContents);
161         }
162
163         return (results.length ? results : null);
164 }
165
166 dojo._xdIsXDomainPath = function(/*string*/relpath) {
167     //summary: Figure out whether the path is local or x-domain
168         //If there is a colon before the first / then, we have a URL with a protocol.
169     
170         var colonIndex = relpath.indexOf(":");
171         var slashIndex = relpath.indexOf("/");
172
173         if(colonIndex > 0 && colonIndex < slashIndex){
174                 return true;
175         }else{
176                 //Is the base script URI-based URL a cross domain URL?
177                 //If so, then the relpath will be evaluated relative to
178                 //baseUrl, and therefore qualify as xdomain.
179                 //Only treat it as xdomain if the page does not have a
180                 //host (file:// url) or if the baseUrl does not match the
181                 //current window's domain.
182                 var url = dojo.baseUrl;
183                 colonIndex = url.indexOf(":");
184                 slashIndex = url.indexOf("/");
185                 if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || url.indexOf("http://" + location.host) != 0)){
186                         return true;
187                 }
188         }
189     return false;     
190 }
191
192 dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
193         //summary: Internal xd loader function. Overrides loadPath() from loader.js.
194         //xd loading requires slightly different behavior from loadPath().
195
196         var currentIsXDomain = dojo._xdIsXDomainPath(relpath);
197     dojo._isXDomain |= currentIsXDomain;
198
199         var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : dojo.baseUrl) + relpath;
200
201         try{
202                 return ((!module || dojo._isXDomain) ? dojo._loadUri(uri, cb, currentIsXDomain, module) : dojo._loadUriAndCheck(uri, module, cb)); //Boolean
203         }catch(e){
204                 console.error(e);
205                 return false; //Boolean
206         }
207 }
208
209 dojo._xdCharSet = "utf-8";
210
211 dojo._loadUri = function(/*String*/uri, /*Function?*/cb, /*boolean*/currentIsXDomain, /*String?*/module){
212         //summary: Internal xd loader function. Overrides loadUri() from loader.js.
213         //              xd loading requires slightly different behavior from loadPath().
214         //description: Wanted to override getText(), but it is used by
215         //              the widget code in too many, synchronous ways right now.
216         if(dojo._loadedUrls[uri]){
217                 return 1; //Boolean
218         }
219
220         //Add the module (resource) to the list of modules.
221         //Only do this work if we have a modlue name. Otherwise, 
222         //it is a non-xd i18n bundle, which can load immediately and does not 
223         //need to be tracked. Also, don't track dojo.i18n, since it is a prerequisite
224         //and will be loaded correctly if we load it right away: it has no dependencies.
225         if(dojo._isXDomain && module && module != "dojo.i18n"){
226                 dojo._xdOrderedReqs.push(module);
227
228                 //Add to waiting resources if it is an xdomain resource.
229                 //Don't add non-xdomain i18n bundles, those get evaled immediately.
230                 if(currentIsXDomain || uri.indexOf("/nls/") == -1){
231                         dojo._xdInFlight[module] = true;
232
233                         //Increment inFlightCount
234                         //This will stop the modulesLoaded from firing all the way.
235                         dojo._inFlightCount++;
236                 }
237
238                 //Start timer
239                 if(!dojo._xdTimer){
240                         if(dojo.isAIR){
241                                 dojo._xdTimer = setInterval(function(){dojo._xdWatchInFlight();}, 100);
242                         }else{
243                                 dojo._xdTimer = setInterval(dojo._scopeName + "._xdWatchInFlight();", 100);
244                         }
245                 }
246                 dojo._xdStartTime = (new Date()).getTime();
247         }
248
249         if (currentIsXDomain){
250                 //Fix name to be a .xd.fileextension name.
251                 var lastIndex = uri.lastIndexOf('.');
252                 if(lastIndex <= 0){
253                         lastIndex = uri.length - 1;
254                 }
255
256                 var xdUri = uri.substring(0, lastIndex) + ".xd";
257                 if(lastIndex != uri.length - 1){
258                         xdUri += uri.substring(lastIndex, uri.length);
259                 }
260
261                 if (dojo.isAIR){
262                         xdUri = xdUri.replace("app:/", "/");
263                 }
264
265                 //Add to script src
266                 var element = document.createElement("script");
267                 element.type = "text/javascript";
268                 if(dojo._xdCharSet){
269                         element.charset = dojo._xdCharSet;
270                 }
271                 element.src = xdUri;
272                 if(!dojo.headElement){
273                         dojo._headElement = document.getElementsByTagName("head")[0];
274
275                         //Head element may not exist, particularly in html
276                         //html 4 or tag soup cases where the page does not
277                         //have a head tag in it. Use html element, since that will exist.
278                         //Seems to be an issue mostly with Opera 9 and to lesser extent Safari 2
279                         if(!dojo._headElement){
280                                 dojo._headElement = document.getElementsByTagName("html")[0];
281                         }
282                 }
283                 dojo._headElement.appendChild(element);
284         }else{
285                 var contents = dojo._getText(uri, null, true);
286                 if(contents == null){ return 0; /*boolean*/}
287                 
288                 //If this is not xdomain, or if loading a i18n resource bundle, then send it down
289                 //the normal eval/callback path.
290                 if(dojo._isXDomain
291                         && uri.indexOf("/nls/") == -1
292                         && module != "dojo.i18n"){
293                         var res = dojo._xdCreateResource(contents, module, uri);
294                         dojo.eval(res);
295                 }else{
296                         if(cb){
297                                 contents = '('+contents+')';
298                         }else{
299                                 //Only do the scoping if no callback. If a callback is specified,
300                                 //it is most likely the i18n bundle stuff.
301                                 contents = dojo._scopePrefix + contents + dojo._scopeSuffix;
302                         }
303                         var value = dojo["eval"](contents+"\r\n//@ sourceURL="+uri);
304                         if(cb){
305                                 cb(value);
306                         }
307                 }
308         }
309
310         //These steps are done in the non-xd loader version of this function.
311         //Maintain these steps to fit in with the existing system.
312         dojo._loadedUrls[uri] = true;
313         dojo._loadedUrls.push(uri);
314         return true; //Boolean
315 }
316
317 dojo._xdResourceLoaded = function(/*Object*/res){
318         //summary: Internal xd loader function. Called by an xd module resource when
319         //it has been loaded via a script tag.
320         
321         //Evaluate the function with scopeArgs for multiversion support.
322         res = res.apply(dojo.global, dojo._scopeArgs);
323
324         //Work through dependencies.
325         var deps = res.depends;
326         var requireList = null;
327         var requireAfterList = null;
328         var provideList = [];
329         if(deps && deps.length > 0){
330                 var dep = null;
331                 var insertHint = 0;
332                 var attachedResource = false;
333                 for(var i = 0; i < deps.length; i++){
334                         dep = deps[i];
335
336                         //Look for specific dependency indicators.
337                         if (dep[0] == "provide"){
338                                 provideList.push(dep[1]);
339                         }else{
340                                 if(!requireList){
341                                         requireList = [];
342                                 }
343                                 if(!requireAfterList){
344                                         requireAfterList = [];
345                                 }
346
347                                 var unpackedDeps = dojo._xdUnpackDependency(dep);
348                                 if(unpackedDeps.requires){
349                                         requireList = requireList.concat(unpackedDeps.requires);
350                                 }
351                                 if(unpackedDeps.requiresAfter){
352                                         requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter);
353                                 }
354                         }
355
356                         //Call the dependency indicator to allow for the normal dojo setup.
357                         //Only allow for one dot reference, for the i18n._preloadLocalizations calls
358                         //(and maybe future, one-dot things).
359                         var depType = dep[0];
360                         var objPath = depType.split(".");
361                         if(objPath.length == 2){
362                                 dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1));
363                         }else{
364                                 dojo[depType].apply(dojo, dep.slice(1));
365                         }
366                 }
367
368
369                 //If loading the debugAtAllCosts module, eval it right away since we need
370                 //its functions to properly load the other modules.
371                 if(provideList.length == 1 && provideList[0] == "dojo._base._loader.loader_debug"){
372                         res.defineResource(dojo);
373                 }else{
374                         //Save off the resource contents for definition later.
375                         var contentIndex = dojo._xdContents.push({
376                                         content: res.defineResource,
377                                         resourceName: res["resourceName"],
378                                         resourcePath: res["resourcePath"],
379                                         isDefined: false
380                                 }) - 1;
381         
382                         //Add provide/requires to dependency map.
383                         for(i = 0; i < provideList.length; i++){
384                                 dojo._xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex };
385                         }
386                 }
387
388                 //Now update the inflight status for any provided resources in this loaded resource.
389                 //Do this at the very end (in a *separate* for loop) to avoid shutting down the 
390                 //inflight timer check too soon.
391                 for(i = 0; i < provideList.length; i++){
392                         dojo._xdInFlight[provideList[i]] = false;
393                 }
394         }
395 }
396
397 dojo._xdLoadFlattenedBundle = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*Object*/bundleData){
398         //summary: Internal xd loader function. Used when loading
399         //a flattened localized bundle via a script tag.
400         locale = locale || "root";
401         var jsLoc = dojo.i18n.normalizeLocale(locale).replace('-', '_');
402         var bundleResource = [moduleName, "nls", bundleName].join(".");
403         var bundle = dojo["provide"](bundleResource);
404         bundle[jsLoc] = bundleData;
405         
406         //Assign the bundle for the original locale(s) we wanted.
407         var mapName = [moduleName, jsLoc, bundleName].join(".");
408         var bundleMap = dojo._xdBundleMap[mapName];
409         if(bundleMap){
410                 for(var param in bundleMap){
411                         bundle[param] = bundleData;
412                 }
413         }
414 };
415
416
417 dojo._xdInitExtraLocales = function(){
418         // Simulate the extra locale work that dojo.requireLocalization does.
419
420         var extra = dojo.config.extraLocale;
421         if(extra){
422                 if(!extra instanceof Array){
423                         extra = [extra];
424                 }
425
426                 dojo._xdReqLoc = dojo.xdRequireLocalization;
427                 dojo.xdRequireLocalization = function(m, b, locale, fLocales){
428                         dojo._xdReqLoc(m,b,locale, fLocales);
429                         if(locale){return;}
430                         for(var i=0; i<extra.length; i++){
431                                 dojo._xdReqLoc(m,b,extra[i], fLocales);
432                         }
433                 };
434         }
435 }
436
437 dojo._xdBundleMap = {};
438
439 dojo.xdRequireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){
440         //summary: Internal xd loader function. The xd version of dojo.requireLocalization.
441         
442
443         //Account for allowing multiple extra locales. Do this here inside the function
444         //since dojo._xdInitExtraLocales() depends on djConfig being set up, but that only
445         //happens after hostenv_browser runs. loader_xd has to come before hostenv_browser
446         //though since hostenv_browser can do a dojo.require for the debug module.
447         if(dojo._xdInitExtraLocales){
448                 dojo._xdInitExtraLocales();
449                 dojo._xdInitExtraLocales = null;
450                 dojo.xdRequireLocalization.apply(dojo, arguments);
451                 return;
452         }
453
454         var locales = availableFlatLocales.split(",");
455         
456         //Find the best-match locale to load.
457         //Assumes dojo.i18n has already been loaded. This is true for xdomain builds,
458         //since it is included in dojo.xd.js.
459         var jsLoc = dojo.i18n.normalizeLocale(locale);
460
461         var bestLocale = "";
462         for(var i = 0; i < locales.length; i++){
463                 //Locale must match from start of string.
464                 if(jsLoc.indexOf(locales[i]) == 0){
465                         if(locales[i].length > bestLocale.length){
466                                 bestLocale = locales[i];
467                         }
468                 }
469         }
470
471         var fixedBestLocale = bestLocale.replace('-', '_');
472         //See if the bundle we are going to use is already loaded.
473         var bundleResource = dojo.getObject([moduleName, "nls", bundleName].join("."));
474         if(!bundleResource || !bundleResource[fixedBestLocale]){
475                 //Need to remember what locale we wanted and which one we actually use.
476                 //Then when we load the one we are actually using, use that bundle for the one
477                 //we originally wanted.
478                 var mapName = [moduleName, (fixedBestLocale||"root"), bundleName].join(".");
479                 var bundleMap = dojo._xdBundleMap[mapName];
480                 if(!bundleMap){
481                         bundleMap = dojo._xdBundleMap[mapName] = {};
482                 }
483                 bundleMap[jsLoc.replace('-', '_')] = true;
484                 
485                 //Do just a normal dojo.require so the resource tracking stuff works as usual.
486                 dojo.require(moduleName + ".nls" + (bestLocale ? "." + bestLocale : "") + "." + bundleName);
487         }
488 }
489
490 // Replace dojo.requireLocalization with a wrapper
491 dojo._xdRealRequireLocalization = dojo.requireLocalization;
492 dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String*/availableFlatLocales){
493     // summary: loads a bundle intelligently based on whether the module is 
494     // local or xd. Overrides the local-case implementation.
495     
496     var modulePath = dojo.moduleUrl(moduleName).toString();
497     if (dojo._xdIsXDomainPath(modulePath)) {
498         // call cross-domain loader
499         return dojo.xdRequireLocalization.apply(dojo, arguments);
500     } else {
501         // call local-loader
502         return dojo._xdRealRequireLocalization.apply(dojo, arguments);
503     }
504 }
505
506 //This is a bit brittle: it has to know about the dojo methods that deal with dependencies
507 //It would be ideal to intercept the actual methods and do something fancy at that point,
508 //but I have concern about knowing which provide to match to the dependency in that case,
509 //since scripts can load whenever they want, and trigger new calls to dojo._xdResourceLoaded().
510 dojo._xdUnpackDependency = function(/*Array*/dep){
511         //summary: Internal xd loader function. Determines what to do with a dependency
512         //that was listed in an xd version of a module contents.
513
514         //Extract the dependency(ies).
515         var newDeps = null;
516         var newAfterDeps = null;
517         switch(dep[0]){
518                 case "requireIf":
519                 case "requireAfterIf":
520                         //First arg (dep[1]) is the test. Depedency is dep[2].
521                         if(dep[1] === true){
522                                 newDeps = [{name: dep[2], content: null}];
523                         }
524                         break;
525                 case "platformRequire":
526                         var modMap = dep[1];
527                         var common = modMap["common"]||[];
528                         newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);  
529                         //Flatten the array of arrays into a one-level deep array.
530                         //Each result could be an array of 3 elements  (the 3 arguments to dojo.require).
531                         //We only need the first one.
532                         if(newDeps){
533                                 for(var i = 0; i < newDeps.length; i++){
534                                         if(newDeps[i] instanceof Array){
535                                                 newDeps[i] = {name: newDeps[i][0], content: null};
536                                         }else{
537                                                 newDeps[i] = {name: newDeps[i], content: null};
538                                         }
539                                 }
540                         }
541                         break;
542                 case "require":
543                         //Just worry about dep[1]
544                         newDeps = [{name: dep[1], content: null}];
545                         break;
546                 case "i18n._preloadLocalizations":
547                         //We can eval these immediately, since they load i18n bundles.
548                         //Since i18n bundles have no dependencies, whenever they are loaded
549                         //in a script tag, they are evaluated immediately, so we do not have to
550                         //treat them has an explicit dependency for the dependency mapping.
551                         //We can call it immediately since dojo.i18n is part of dojo.xd.js.
552                         dojo.i18n._preloadLocalizations.apply(dojo.i18n._preloadLocalizations, dep.slice(1));
553                         break;
554         }
555
556         //The requireIf and requireAfterIf needs to be evaluated after the current resource is evaluated.
557         if(dep[0] == "requireAfterIf" || dep[0] == "requireIf"){
558                 newAfterDeps = newDeps;
559                 newDeps = null;
560         }
561         return {requires: newDeps, requiresAfter: newAfterDeps}; //Object
562 }
563
564 dojo._xdWalkReqs = function(){
565         //summary: Internal xd loader function. 
566         //Walks the requires and evaluates module resource contents in
567         //the right order.
568         var reqChain = null;
569         var req;
570         for(var i = 0; i < dojo._xdOrderedReqs.length; i++){
571                 req = dojo._xdOrderedReqs[i];
572                 if(dojo._xdDepMap[req]){
573                         reqChain = [req];
574                         reqChain[req] = true; //Allow for fast lookup of the req in the array
575                         dojo._xdEvalReqs(reqChain);
576                 }
577         }
578 }
579
580 dojo._xdEvalReqs = function(/*Array*/reqChain){
581         //summary: Internal xd loader function. 
582         //Does a depth first, breadth second search and eval of required modules.
583         while(reqChain.length > 0){
584                 var req = reqChain[reqChain.length - 1];
585                 var res = dojo._xdDepMap[req];
586                 var i, reqs, nextReq;
587                 if(res){
588                         //Trace down any requires for this resource.
589                         //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack
590                         reqs = res.requires;
591                         if(reqs && reqs.length > 0){
592                                 for(i = 0; i < reqs.length; i++){
593                                         nextReq = reqs[i].name;
594                                         if(nextReq && !reqChain[nextReq]){
595                                                 //New req depedency. Follow it down.
596                                                 reqChain.push(nextReq);
597                                                 reqChain[nextReq] = true;
598                                                 dojo._xdEvalReqs(reqChain);
599                                         }
600                                 }
601                         }
602                         //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack
603
604                         //Evaluate the resource.
605                         var contents = dojo._xdContents[res.contentIndex];
606                         if(!contents.isDefined){
607                                 var content = contents.content;
608                                 content["resourceName"] = contents["resourceName"];
609                                 content["resourcePath"] = contents["resourcePath"];
610                                 dojo._xdDefList.push(content);
611                                 contents.isDefined = true;
612                         }
613                         dojo._xdDepMap[req] = null;
614
615                         //Trace down any requireAfters for this resource.
616                         //START dojo._xdTraceReqs() inlining for small Safari 2.0 call stack
617                         reqs = res.requiresAfter;
618                         if(reqs && reqs.length > 0){
619                                 for(i = 0; i < reqs.length; i++){
620                                         nextReq = reqs[i].name;
621                                         if(nextReq && !reqChain[nextReq]){
622                                                 //New req depedency. Follow it down.
623                                                 reqChain.push(nextReq);
624                                                 reqChain[nextReq] = true;
625                                                 dojo._xdEvalReqs(reqChain);
626                                         }
627                                 }
628                         }
629                         //END dojo._xdTraceReqs() inlining for small Safari 2.0 call stack
630                 }
631
632                 //Done with that require. Remove it and go to the next one.
633                 reqChain.pop();
634         }
635 }
636
637 dojo._xdWatchInFlight = function(){
638         //summary: Internal xd loader function.
639         //Monitors in-flight requests for xd module resources.
640
641         var noLoads = "";
642         var waitInterval = (dojo.config.xdWaitSeconds || 15) * 1000;
643         var expired = (dojo._xdStartTime + waitInterval) < (new Date()).getTime();
644
645         //If any xdInFlight are true, then still waiting for something to load.
646         //Come back later. If we timed out, report the things that did not load.
647         for(var param in dojo._xdInFlight){
648                 if(dojo._xdInFlight[param] === true){
649                         if(expired){
650                                 noLoads += param + " ";
651                         }else{
652                                 return;
653                         }
654                 }
655         }
656
657         //All done. Clean up and notify.
658         dojo._xdClearInterval();
659
660         if(expired){
661                 throw "Could not load cross-domain resources: " + noLoads;
662         }
663
664         dojo._xdWalkReqs();
665         
666         var defLength = dojo._xdDefList.length;
667         for(var i= 0; i < defLength; i++){
668                 var content = dojo._xdDefList[i];
669                 if(dojo.config["debugAtAllCosts"] && content["resourceName"]){
670                         if(!dojo["_xdDebugQueue"]){
671                                 dojo._xdDebugQueue = [];
672                         }
673                         dojo._xdDebugQueue.push({resourceName: content.resourceName, resourcePath: content.resourcePath});
674                 }else{
675                         //Evaluate the resource to bring it into being.
676                         //Pass in scope args to allow multiple versions of modules in a page.   
677                         content.apply(dojo.global, dojo._scopeArgs);
678                 }
679         }
680
681         //Evaluate any resources that were not evaled before.
682         //This normally shouldn't happen with proper dojo.provide and dojo.require
683         //usage, but providing it just in case. Note that these may not be executed
684         //in the original order that the developer intended.
685         for(i = 0; i < dojo._xdContents.length; i++){
686                 var current = dojo._xdContents[i];
687                 if(current.content && !current.isDefined){
688                         //Pass in scope args to allow multiple versions of modules in a page.   
689                         current.content.apply(dojo.global, dojo._scopeArgs);
690                 }
691         }
692
693         //Clean up for the next round of xd loading.
694         dojo._xdReset();
695
696         if(dojo["_xdDebugQueue"] && dojo._xdDebugQueue.length > 0){
697                 dojo._xdDebugFileLoaded();
698         }else{
699                 dojo._xdNotifyLoaded();
700         }
701 }
702
703 dojo._xdNotifyLoaded = function(){
704         //Clear inflight count so we will finally do finish work.
705
706         //Just having a legitimate status (true or false) for an inflight item
707         //means that it is still being processed. Do the typeof test
708         //to avoid bad JavaScript that might tinker with Object.prototype.
709         for(var prop in dojo._xdInFlight){
710                 if(typeof dojo._xdInFlight[prop] == "boolean"){
711                         return;
712                 }
713         }
714
715         dojo._inFlightCount = 0; 
716
717         //Only trigger call loaded if dj_load_init has run. 
718         if(dojo._initFired && !dojo._loadNotifying){ 
719                 dojo._callLoaded();
720         }
721 }
722
723 }