|
23 | 23 | } |
24 | 24 | } |
25 | 25 |
|
| 26 | + //regexp for matching nls (i18n) module names. |
| 27 | + var nlsRegExp = /(^.*(^|\.)nls(\.|$))([^\.]*)\.?([^\.]*)/; |
| 28 | + |
26 | 29 | /** |
27 | 30 | * The function that loads modules or executes code that has dependencies |
28 | 31 | * on other modules. |
|
34 | 37 | if (typeof name == "string") { |
35 | 38 | //Defining a module. |
36 | 39 | //First check if there are no dependencies, and adjust args. |
37 | | - if (typeof deps == "function" || deps instanceof Function) { |
| 40 | + if (!(deps instanceof Array) && typeof deps != "array") { |
38 | 41 | contextName = callback; |
39 | 42 | callback = deps; |
40 | 43 | deps = []; |
|
43 | 46 | contextName = contextName || run._currContextName; |
44 | 47 |
|
45 | 48 | //If module already defined for context, leave. |
46 | | - var context = contexts[contextName]; |
| 49 | + var context = run._contexts[contextName]; |
47 | 50 | if (context && context.specified && context.specified[name]) { |
48 | 51 | return run; |
49 | 52 | } |
|
76 | 79 |
|
77 | 80 | contextName = contextName || run._currContextName; |
78 | 81 |
|
79 | | - if (contextName != "run._currContextName") { |
| 82 | + if (contextName != run._currContextName) { |
80 | 83 | //If nothing is waiting on being loaded in the current context, |
81 | 84 | //then switch run._currContextName to current contextName. |
82 | | - var loaded = contexts[run._currContextName] && contexts[run._currContextName].loaded, |
| 85 | + var loaded = run._contexts[run._currContextName] && run._contexts[run._currContextName].loaded, |
83 | 86 | empty = {}, |
84 | 87 | canSetContext = true; |
85 | 88 | if (loaded) { |
|
98 | 101 | } |
99 | 102 |
|
100 | 103 | //Grab the context, or create a new one for the given context name. |
101 | | - var context = contexts[contextName] || (contexts[contextName] = { |
| 104 | + var context = run._contexts[contextName] || (run._contexts[contextName] = { |
102 | 105 | waiting: [], |
| 106 | + nlsWaiting: {}, |
103 | 107 | baseUrl: run.baseUrl || "./", |
| 108 | + locale: typeof navigator == "undefined"? "root" : |
| 109 | + (navigator.language || navigator.userLanguage || "root").toLowerCase(), |
104 | 110 | paths: {}, |
105 | 111 | waitSeconds: 7, |
106 | 112 | specified: { |
|
120 | 126 | } |
121 | 127 | return run.apply(window, args); |
122 | 128 | } |
123 | | - } |
| 129 | + }, |
| 130 | + nls: {} |
124 | 131 | }); |
125 | 132 |
|
126 | 133 | //If have a config object, update the context object with |
|
147 | 154 | } |
148 | 155 | } |
149 | 156 | } |
| 157 | + |
| 158 | + if (config.locale) { |
| 159 | + context.locale = config.locale.toLowerCase(); |
| 160 | + } |
150 | 161 | } |
151 | 162 |
|
152 | 163 | //Store the module for later evaluation. |
|
167 | 178 | context.specified[name] = true; |
168 | 179 | } |
169 | 180 |
|
| 181 | + //If the callback is not an actual function, it means it already |
| 182 | + //has the definition of the module as a literal value. |
| 183 | + if (callback && typeof callback != "function" && !(callback instanceof Function)) { |
| 184 | + context.defined[name] = callback; |
| 185 | + } |
| 186 | + |
| 187 | + |
| 188 | + //See if the modules is an nls module and handle it special. |
| 189 | + var match = nlsRegExp.exec(name); |
| 190 | + if (match) { |
| 191 | + //Reconstruct the master bundle name from parts of the regexp match |
| 192 | + //nlsRegExp.exec("foo.bar.baz.nls.en-ca.foo") gives: |
| 193 | + //["foo.bar.baz.nls.en-ca.foo", "foo.bar.baz.nls.", ".", ".", "en-ca", "foo"] |
| 194 | + //nlsRegExp.exec("foo.bar.baz.nls.foo") gives: |
| 195 | + //["foo.bar.baz.nls.foo", "foo.bar.baz.nls.", ".", ".", "foo", ""] |
| 196 | + //so, if match[5] is blank, it means this is the top bundle definition, |
| 197 | + //so it does not have to be handled. Only deal with ones that have a locale |
| 198 | + //(a match[4] value but no match[5]) |
| 199 | + if (match[5]) { |
| 200 | + var master = match[1] + match[5]; |
| 201 | + |
| 202 | + //Track what locale bundle need to be generated once all the modules load. |
| 203 | + var nlsw = (context.nlsWaiting[master] || (context.nlsWaiting[master] = {})); |
| 204 | + nlsw[match[4]] = 1; |
| 205 | + |
| 206 | + var bundle = context.nls[master]; |
| 207 | + if (!bundle) { |
| 208 | + //No master bundle yet, ask for it. |
| 209 | + context.defined.run([master]); |
| 210 | + bundle = context.nls[master] = {}; |
| 211 | + } |
| 212 | + //For nls modules, the callback is just a regular object, |
| 213 | + //so save it off in the bundle now. |
| 214 | + bundle[match[4]] = callback; |
| 215 | + } |
| 216 | + } |
| 217 | + |
170 | 218 | //Figure out if all the modules are loaded. If the module is not |
171 | 219 | //being loaded or already loaded, add it to the "to load" list, |
172 | 220 | //and request it to be loaded. |
173 | 221 | var needLoad = false; |
174 | 222 | for (var i = 0, dep; dep = deps[i]; i++) { |
175 | | - if (!(dep in context.loaded)) { |
176 | | - context.loaded[dep] = false; |
177 | | - run.load(dep, contextName); |
178 | | - needLoad = true; |
| 223 | + //If it is a string, then a plain dependency |
| 224 | + if (typeof dep == "string") { |
| 225 | + if (!(dep in context.loaded)) { |
| 226 | + context.loaded[dep] = false; |
| 227 | + run.load(dep, contextName); |
| 228 | + needLoad = true; |
| 229 | + } |
| 230 | + } else { |
| 231 | + //dep is an object, so it is an i18n nls thing. |
| 232 | + //Track it in the nls section of the context. |
| 233 | + //It may have already been created via a specific locale |
| 234 | + //request, so just mixin values in that case, to preserve |
| 235 | + //the specific locale bundle object. |
| 236 | + var bundle = context.nls[name]; |
| 237 | + if (bundle) { |
| 238 | + run.mixin(bundle, dep); |
| 239 | + } else { |
| 240 | + context.nls[name] = dep; |
| 241 | + } |
| 242 | + |
| 243 | + //Break apart the locale to get the parts. |
| 244 | + var parts = context.locale.split("-"); |
| 245 | + |
| 246 | + //Now see what bundles exist for each country/locale. |
| 247 | + //Want to walk up the chain, so if locale is en-us-foo, |
| 248 | + //look for en-us-foo, en-us, en, then root. |
| 249 | + var toLoad = []; |
| 250 | + var longestMatch = null; |
| 251 | + var nlsw = context.nlsWaiting[name] || (context.nlsWaiting[name] = {}); |
| 252 | + for (var j = parts.length; j > -1; j--) { |
| 253 | + var loc = j == 0 ? "root" : parts.slice(0, j).join("-"); |
| 254 | + var val = dep[loc]; |
| 255 | + if (val) { |
| 256 | + //Store which bundle to use for the default bundle definition. |
| 257 | + if (!nlsw.__match) { |
| 258 | + nlsw.__match = loc; |
| 259 | + } |
| 260 | + |
| 261 | + //Track that the locale needs to be resolved with its parts. |
| 262 | + nlsw[loc] = 1; |
| 263 | + |
| 264 | + //If locale value is a string, it means it is a resource that |
| 265 | + //needs to be loaded. Track it to load if it has not already |
| 266 | + //been asked for. |
| 267 | + if (typeof val == "string" |
| 268 | + && !context.specified[val] |
| 269 | + && !(val in context.loaded)) { |
| 270 | + toLoad.push(val); |
| 271 | + } |
| 272 | + } |
| 273 | + } |
| 274 | + |
| 275 | + //Load any bundles that are still needed. |
| 276 | + if (toLoad.length) { |
| 277 | + context.defined.run(toLoad); |
| 278 | + } |
179 | 279 | } |
180 | 280 | } |
181 | 281 |
|
|
190 | 290 | //default context too. |
191 | 291 | var defContextName = "_runDefault"; |
192 | 292 | run._currContextName = defContextName; |
193 | | - var contexts = {}; |
| 293 | + run._contexts = {}; |
194 | 294 | var contextLoads = []; |
195 | 295 |
|
196 | 296 | //Set state for page loading. |
|
227 | 327 | //First derive the path name for the module. |
228 | 328 | var url = run.convertNameToPath(moduleName, contextName); |
229 | 329 | run.attach(url, contextName, moduleName); |
230 | | - contexts[contextName].startTime = (new Date()).getTime(); |
| 330 | + run._contexts[contextName].startTime = (new Date()).getTime(); |
231 | 331 | } |
232 | 332 | } |
233 | 333 |
|
|
242 | 342 | return moduleName; |
243 | 343 | } else { |
244 | 344 | //A module that needs to be converted to a path. |
245 | | - var paths = contexts[contextName].paths; |
| 345 | + var paths = run._contexts[contextName].paths; |
246 | 346 | var syms = moduleName.split("."); |
247 | 347 | for (var i = syms.length; i > 0; i--) { |
248 | 348 | var parentModule = syms.slice(0, i).join("."); |
|
257 | 357 |
|
258 | 358 | //Join the path parts together, then figure out if baseUrl is needed. |
259 | 359 | var url = syms.join("/") + ".js"; |
260 | | - return ((url.charAt(0) == '/' || url.match(/^\w+:/)) ? "" : contexts[contextName].baseUrl) + url; |
| 360 | + return ((url.charAt(0) == '/' || url.match(/^\w+:/)) ? "" : run._contexts[contextName].baseUrl) + url; |
261 | 361 | } |
262 | 362 | } |
263 | 363 |
|
|
266 | 366 | * new ones in right dependency order. |
267 | 367 | */ |
268 | 368 | run.checkLoaded = function(contextName) { |
269 | | - var context = contexts[contextName || run._currContextName]; |
| 369 | + var context = run._contexts[contextName || run._currContextName]; |
270 | 370 | var waitInterval = context.waitSeconds * 1000; |
271 | 371 | //It is possible to disable the wait interval by using waitSeconds of 0. |
272 | 372 | var expired = waitInterval && (context.startTime + waitInterval) < (new Date()).getTime(); |
|
307 | 407 | //Resolve dependencies. First clean up state because the evaluation |
308 | 408 | //of modules might create new loading tasks, so need to reset. |
309 | 409 | var waiting = context.waiting; |
| 410 | + var nlsWaiting = context.nlsWaiting; |
310 | 411 | context.waiting = []; |
| 412 | + context.nlsWaiting = {}; |
311 | 413 | context.loaded = {}; |
312 | | - |
| 414 | + |
| 415 | + //First, properly mix in any nls bundles waiting to happen. |
| 416 | + //Use an empty object to detect other bad JS code that modifies |
| 417 | + //Object.prototype. |
| 418 | + var empty = {}; |
| 419 | + for (var prop in nlsWaiting) { |
| 420 | + if (!(prop in empty)) { |
| 421 | + //Each property is a master bundle name. |
| 422 | + var master = prop; |
| 423 | + var msWaiting = nlsWaiting[prop]; |
| 424 | + var bundle = context.nls[master]; |
| 425 | + var defLoc = null; |
| 426 | + |
| 427 | + //Create the module name parts from the master name. So, if master |
| 428 | + //is foo.nls.bar, then the parts should be prefix: "foo.nls", |
| 429 | + // suffix: "bar", and the final locale's module name will be foo.nls.locale.bar |
| 430 | + var parts = master.split("."); |
| 431 | + var modulePrefix = parts.slice(0, parts.length - 1).join("."); |
| 432 | + var moduleSuffix = parts[parts.length - 1]; |
| 433 | + //Cycle through the locale props on the waiting object and combine |
| 434 | + //the locales together. |
| 435 | + for (var loc in msWaiting) { |
| 436 | + if (!(loc in empty)) { |
| 437 | + if (loc == "__match") { |
| 438 | + //Found default locale to use for the top-level bundle name. |
| 439 | + defLoc = msWaiting[loc]; |
| 440 | + } else { |
| 441 | + //Mix in the properties of this locale together. |
| 442 | + //Split the locale into pieces. |
| 443 | + var mixed = {}; |
| 444 | + var parts = loc.split("-"); |
| 445 | + for (var i = parts.length; i > 0; i--) { |
| 446 | + var locPart = parts.slice(0, i).join("-"); |
| 447 | + if (locPart !== "root" && bundle[locPart]) { |
| 448 | + run.mixin(mixed, bundle[locPart]); |
| 449 | + } |
| 450 | + } |
| 451 | + if (bundle["root"]) { |
| 452 | + run.mixin(mixed, bundle["root"]); |
| 453 | + } |
| 454 | + |
| 455 | + context.defined[modulePrefix + "." + loc + "." + moduleSuffix] = mixed; |
| 456 | + } |
| 457 | + } |
| 458 | + } |
| 459 | + |
| 460 | + //Finally define the default locale. Wait to the end of the property |
| 461 | + //loop above so that the default locale bundle has been properly mixed |
| 462 | + //together. |
| 463 | + context.defined[master] = context.defined[modulePrefix + "." + defLoc + "." + moduleSuffix]; |
| 464 | + } |
| 465 | + } |
| 466 | + |
313 | 467 | //Walk the dependencies, doing a depth first search. |
314 | 468 | var orderedModules = []; |
315 | 469 | for (var i = 0, module; module = waiting[i]; i++) { |
|
333 | 487 | var depModule = context.defined[dep] || (context.defined[dep] = {}); |
334 | 488 | args.push(depModule); |
335 | 489 | } |
336 | | - if (module.callback) { |
337 | | - var ret = module.callback.apply(window, args); |
| 490 | + |
| 491 | + //Call the callback to define the module, if necessary. |
| 492 | + var cb = module.callback; |
| 493 | + if (cb && (typeof cb == "function" || cb instanceof Function)) { |
| 494 | + var ret = cb.apply(window, args); |
338 | 495 | if (name) { |
339 | 496 | var modDef = context.defined[name]; |
340 | 497 | if (modDef && ret) { |
341 | 498 | //Mix in the contents of the ret object. This is done for |
342 | 499 | //cases where we passed the placeholder module to a circular |
343 | 500 | //dependency. |
344 | | - //Use an empty placeholder object to avoid bad JS code that |
345 | | - //adds things to Object.prototype. |
346 | | - var empty = {}; |
347 | | - for (var prop in ret) { |
348 | | - if (!(ret in empty)) { |
349 | | - modDef[prop] = ret[prop]; |
350 | | - } |
351 | | - } |
| 501 | + run.mixin(modDef, ret); |
352 | 502 | } else { |
353 | 503 | context.defined[name] = ret; |
354 | 504 | } |
|
440 | 590 | var moduleName = node.getAttribute("data-runmodule"); |
441 | 591 |
|
442 | 592 | //Mark the module loaded. |
443 | | - contexts[contextName].loaded[moduleName] = true; |
| 593 | + run._contexts[contextName].loaded[moduleName] = true; |
444 | 594 |
|
445 | 595 | run.checkLoaded(contextName); |
446 | 596 |
|
|
482 | 632 | return null; |
483 | 633 | } |
484 | 634 |
|
| 635 | + /** |
| 636 | + * Simple function to mix in properties from source into target, |
| 637 | + * but only if target does not already have a property of the same name. |
| 638 | + */ |
| 639 | + run.mixin = function(target, source) { |
| 640 | + //Use an empty object to avoid other bad JS code that modifies |
| 641 | + //Object.prototype. |
| 642 | + var empty = {}; |
| 643 | + for (var prop in source) { |
| 644 | + if (!(prop in target)) { |
| 645 | + target[prop] = source[prop]; |
| 646 | + } |
| 647 | + } |
| 648 | + return run; |
| 649 | + } |
| 650 | + |
485 | 651 | //****** START page load functionality **************** |
486 | 652 | //Set up page on load callbacks. May separate this out. |
487 | 653 | var isPageLoaded = !isBrowser; |
|
0 commit comments