[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('loader-base', function (Y, NAME) { 9 10 /** 11 * The YUI loader core 12 * @module loader 13 * @submodule loader-base 14 */ 15 16 (function() { 17 var VERSION = Y.version, 18 BUILD = '/build/', 19 ROOT = VERSION + '/', 20 CDN_BASE = Y.Env.base, 21 GALLERY_VERSION = 'gallery-2014.05.29-15-46', 22 TNT = '2in3', 23 TNT_VERSION = '4', 24 YUI2_VERSION = '2.9.0', 25 COMBO_BASE = CDN_BASE + 'combo?', 26 META = { 27 version: VERSION, 28 root: ROOT, 29 base: Y.Env.base, 30 comboBase: COMBO_BASE, 31 skin: { 32 defaultSkin: 'sam', 33 base: 'assets/skins/', 34 path: 'skin.css', 35 after: [ 36 'cssreset', 37 'cssfonts', 38 'cssgrids', 39 'cssbase', 40 'cssreset-context', 41 'cssfonts-context' 42 ] 43 }, 44 groups: {}, 45 patterns: {} 46 }, 47 groups = META.groups, 48 yui2Update = function(tnt, yui2, config) { 49 var root = TNT + '.' + 50 (tnt || TNT_VERSION) + '/' + 51 (yui2 || YUI2_VERSION) + BUILD, 52 base = (config && config.base) ? config.base : CDN_BASE, 53 combo = (config && config.comboBase) ? config.comboBase : COMBO_BASE; 54 55 groups.yui2.base = base + root; 56 groups.yui2.root = root; 57 groups.yui2.comboBase = combo; 58 }, 59 galleryUpdate = function(tag, config) { 60 var root = (tag || GALLERY_VERSION) + BUILD, 61 base = (config && config.base) ? config.base : CDN_BASE, 62 combo = (config && config.comboBase) ? config.comboBase : COMBO_BASE; 63 64 groups.gallery.base = base + root; 65 groups.gallery.root = root; 66 groups.gallery.comboBase = combo; 67 }; 68 69 70 groups[VERSION] = {}; 71 72 groups.gallery = { 73 ext: false, 74 combine: true, 75 comboBase: COMBO_BASE, 76 update: galleryUpdate, 77 patterns: { 78 'gallery-': {}, 79 'lang/gallery-': {}, 80 'gallerycss-': { 81 type: 'css' 82 } 83 } 84 }; 85 86 groups.yui2 = { 87 combine: true, 88 ext: false, 89 comboBase: COMBO_BASE, 90 update: yui2Update, 91 patterns: { 92 'yui2-': { 93 configFn: function(me) { 94 if (/-skin|reset|fonts|grids|base/.test(me.name)) { 95 me.type = 'css'; 96 me.path = me.path.replace(/\.js/, '.css'); 97 // this makes skins in builds earlier than 98 // 2.6.0 work as long as combine is false 99 me.path = me.path.replace(/\/yui2-skin/, 100 '/assets/skins/sam/yui2-skin'); 101 } 102 } 103 } 104 } 105 }; 106 107 galleryUpdate(); 108 yui2Update(); 109 110 if (YUI.Env[VERSION]) { 111 Y.mix(META, YUI.Env[VERSION], false, [ 112 'modules', 113 'groups', 114 'skin' 115 ], 0, true); 116 } 117 118 YUI.Env[VERSION] = META; 119 }()); 120 /*jslint forin: true, maxlen: 350 */ 121 122 /** 123 * Loader dynamically loads script and css files. It includes the dependency 124 * information for the version of the library in use, and will automatically pull in 125 * dependencies for the modules requested. It can also load the 126 * files from the Yahoo! CDN, and it can utilize the combo service provided on 127 * this network to reduce the number of http connections required to download 128 * YUI files. 129 * 130 * @module loader 131 * @main loader 132 * @submodule loader-base 133 */ 134 135 var NOT_FOUND = {}, 136 NO_REQUIREMENTS = [], 137 MAX_URL_LENGTH = 1024, 138 GLOBAL_ENV = YUI.Env, 139 GLOBAL_LOADED = GLOBAL_ENV._loaded, 140 CSS = 'css', 141 JS = 'js', 142 INTL = 'intl', 143 DEFAULT_SKIN = 'sam', 144 VERSION = Y.version, 145 ROOT_LANG = '', 146 YObject = Y.Object, 147 oeach = YObject.each, 148 yArray = Y.Array, 149 _queue = GLOBAL_ENV._loaderQueue, 150 META = GLOBAL_ENV[VERSION], 151 SKIN_PREFIX = 'skin-', 152 L = Y.Lang, 153 ON_PAGE = GLOBAL_ENV.mods, 154 modulekey, 155 _path = function(dir, file, type, nomin) { 156 var path = dir + '/' + file; 157 if (!nomin) { 158 path += '-min'; 159 } 160 path += '.' + (type || CSS); 161 162 return path; 163 }; 164 165 166 if (!YUI.Env._cssLoaded) { 167 YUI.Env._cssLoaded = {}; 168 } 169 170 171 /** 172 * The component metadata is stored in Y.Env.meta. 173 * Part of the loader module. 174 * @property meta 175 * @for YUI 176 */ 177 Y.Env.meta = META; 178 179 /** 180 * Loader dynamically loads script and css files. It includes the dependency 181 * info for the version of the library in use, and will automatically pull in 182 * dependencies for the modules requested. It can load the 183 * files from the Yahoo! CDN, and it can utilize the combo service provided on 184 * this network to reduce the number of http connections required to download 185 * YUI files. You can also specify an external, custom combo service to host 186 * your modules as well. 187 188 var Y = YUI(); 189 var loader = new Y.Loader({ 190 filter: 'debug', 191 base: '../../', 192 root: 'build/', 193 combine: true, 194 require: ['node', 'dd', 'console'] 195 }); 196 var out = loader.resolve(true); 197 198 * If the Loader needs to be patched before it is used for the first time, it 199 * should be done through the `doBeforeLoader` hook. Simply make the patch 200 * available via configuration before YUI is loaded: 201 202 YUI_config = YUI_config || {}; 203 YUI_config.doBeforeLoader = function (config) { 204 var resolve = this.context.Loader.prototype.resolve; 205 this.context.Loader.prototype.resolve = function () { 206 // do something here 207 return resolve.apply(this, arguments); 208 }; 209 }; 210 211 * @constructor 212 * @class Loader 213 * @param {Object} config an optional set of configuration options. 214 * @param {String} config.base The base dir which to fetch this module from 215 * @param {String} config.comboBase The Combo service base path. Ex: `http://yui.yahooapis.com/combo?` 216 * @param {String} config.root The root path to prepend to module names for the combo service. Ex: `2.5.2/build/` 217 * @param {String|Object} config.filter A filter to apply to result urls. <a href="#property_filter">See filter property</a> 218 * @param {Object} config.filters Per-component filter specification. If specified for a given component, this overrides the filter config. 219 * @param {Boolean} config.combine Use a combo service to reduce the number of http connections required to load your dependencies 220 * @param {Boolean} [config.async=true] Fetch files in async 221 * @param {Array} config.ignore: A list of modules that should never be dynamically loaded 222 * @param {Array} config.force A list of modules that should always be loaded when required, even if already present on the page 223 * @param {HTMLElement|String} config.insertBefore Node or id for a node that should be used as the insertion point for new nodes 224 * @param {Object} config.jsAttributes Object literal containing attributes to add to script nodes 225 * @param {Object} config.cssAttributes Object literal containing attributes to add to link nodes 226 * @param {Number} config.timeout The number of milliseconds before a timeout occurs when dynamically loading nodes. If not set, there is no timeout 227 * @param {Object} config.context Execution context for all callbacks 228 * @param {Function} config.onSuccess Callback for the 'success' event 229 * @param {Function} config.onFailure Callback for the 'failure' event 230 * @param {Function} config.onTimeout Callback for the 'timeout' event 231 * @param {Function} config.onProgress Callback executed each time a script or css file is loaded 232 * @param {Object} config.modules A list of module definitions. See <a href="#method_addModule">Loader.addModule</a> for the supported module metadata 233 * @param {Object} config.groups A list of group definitions. Each group can contain specific definitions for `base`, `comboBase`, `combine`, and accepts a list of `modules`. 234 * @param {String} config.2in3 The version of the YUI 2 in 3 wrapper to use. The intrinsic support for YUI 2 modules in YUI 3 relies on versions of the YUI 2 components inside YUI 3 module wrappers. These wrappers change over time to accomodate the issues that arise from running YUI 2 in a YUI 3 sandbox. 235 * @param {String} config.yui2 When using the 2in3 project, you can select the version of YUI 2 to use. Valid values are `2.2.2`, `2.3.1`, `2.4.1`, `2.5.2`, `2.6.0`, `2.7.0`, `2.8.0`, `2.8.1` and `2.9.0` [default] -- plus all versions of YUI 2 going forward. 236 * @param {Function} config.doBeforeLoader An optional hook that allows for the patching of the loader instance. The `Y` instance is available as `this.context` and the only argument to the function is the Loader configuration object. 237 */ 238 Y.Loader = function(o) { 239 240 var self = this; 241 242 //Catch no config passed. 243 o = o || {}; 244 245 modulekey = META.md5; 246 247 /** 248 * Internal callback to handle multiple internal insert() calls 249 * so that css is inserted prior to js 250 * @property _internalCallback 251 * @private 252 */ 253 // self._internalCallback = null; 254 255 /** 256 * Callback that will be executed when the loader is finished 257 * with an insert 258 * @method onSuccess 259 * @type function 260 */ 261 // self.onSuccess = null; 262 263 /** 264 * Callback that will be executed if there is a failure 265 * @method onFailure 266 * @type function 267 */ 268 // self.onFailure = null; 269 270 /** 271 * Callback executed each time a script or css file is loaded 272 * @method onProgress 273 * @type function 274 */ 275 // self.onProgress = null; 276 277 /** 278 * Callback that will be executed if a timeout occurs 279 * @method onTimeout 280 * @type function 281 */ 282 // self.onTimeout = null; 283 284 /** 285 * The execution context for all callbacks 286 * @property context 287 * @default {YUI} the YUI instance 288 */ 289 self.context = Y; 290 291 // Hook that allows the patching of loader 292 if (o.doBeforeLoader) { 293 o.doBeforeLoader.apply(self, arguments); 294 } 295 296 /** 297 * Data that is passed to all callbacks 298 * @property data 299 */ 300 // self.data = null; 301 302 /** 303 * Node reference or id where new nodes should be inserted before 304 * @property insertBefore 305 * @type string|HTMLElement 306 */ 307 // self.insertBefore = null; 308 309 /** 310 * The charset attribute for inserted nodes 311 * @property charset 312 * @type string 313 * @deprecated , use cssAttributes or jsAttributes. 314 */ 315 // self.charset = null; 316 317 /** 318 * An object literal containing attributes to add to link nodes 319 * @property cssAttributes 320 * @type object 321 */ 322 // self.cssAttributes = null; 323 324 /** 325 * An object literal containing attributes to add to script nodes 326 * @property jsAttributes 327 * @type object 328 */ 329 // self.jsAttributes = null; 330 331 /** 332 * The base directory. 333 * @property base 334 * @type string 335 * @default http://yui.yahooapis.com/[YUI VERSION]/build/ 336 */ 337 self.base = Y.Env.meta.base + Y.Env.meta.root; 338 339 /** 340 * Base path for the combo service 341 * @property comboBase 342 * @type string 343 * @default http://yui.yahooapis.com/combo? 344 */ 345 self.comboBase = Y.Env.meta.comboBase; 346 347 /* 348 * Base path for language packs. 349 */ 350 // self.langBase = Y.Env.meta.langBase; 351 // self.lang = ""; 352 353 /** 354 * If configured, the loader will attempt to use the combo 355 * service for YUI resources and configured external resources. 356 * @property combine 357 * @type boolean 358 * @default true if a base dir isn't in the config 359 */ 360 self.combine = o.base && 361 (o.base.indexOf(self.comboBase.substr(0, 20)) > -1); 362 363 /** 364 * The default seperator to use between files in a combo URL 365 * @property comboSep 366 * @type {String} 367 * @default Ampersand 368 */ 369 self.comboSep = '&'; 370 /** 371 * Max url length for combo urls. The default is 1024. This is the URL 372 * limit for the Yahoo! hosted combo servers. If consuming 373 * a different combo service that has a different URL limit 374 * it is possible to override this default by supplying 375 * the maxURLLength config option. The config option will 376 * only take effect if lower than the default. 377 * 378 * @property maxURLLength 379 * @type int 380 */ 381 self.maxURLLength = MAX_URL_LENGTH; 382 383 /** 384 * Ignore modules registered on the YUI global 385 * @property ignoreRegistered 386 * @default false 387 */ 388 self.ignoreRegistered = o.ignoreRegistered; 389 390 /** 391 * Root path to prepend to module path for the combo 392 * service 393 * @property root 394 * @type string 395 * @default [YUI VERSION]/build/ 396 */ 397 self.root = Y.Env.meta.root; 398 399 /** 400 * Timeout value in milliseconds. If set, self value will be used by 401 * the get utility. the timeout event will fire if 402 * a timeout occurs. 403 * @property timeout 404 * @type int 405 */ 406 self.timeout = 0; 407 408 /** 409 * A list of modules that should not be loaded, even if 410 * they turn up in the dependency tree 411 * @property ignore 412 * @type string[] 413 */ 414 // self.ignore = null; 415 416 /** 417 * A list of modules that should always be loaded, even 418 * if they have already been inserted into the page. 419 * @property force 420 * @type string[] 421 */ 422 // self.force = null; 423 424 self.forceMap = {}; 425 426 /** 427 * Should we allow rollups 428 * @property allowRollup 429 * @type boolean 430 * @default false 431 */ 432 self.allowRollup = false; 433 434 /** 435 * A filter to apply to result urls. This filter will modify the default 436 * path for all modules. The default path for the YUI library is the 437 * minified version of the files (e.g., event-min.js). The filter property 438 * can be a predefined filter or a custom filter. The valid predefined 439 * filters are: 440 * <dl> 441 * <dt>DEBUG</dt> 442 * <dd>Selects the debug versions of the library (e.g., event-debug.js). 443 * This option will automatically include the Logger widget</dd> 444 * <dt>RAW</dt> 445 * <dd>Selects the non-minified version of the library (e.g., event.js). 446 * </dd> 447 * </dl> 448 * You can also define a custom filter, which must be an object literal 449 * containing a search expression and a replace string: 450 * 451 * myFilter: { 452 * 'searchExp': "-min\\.js", 453 * 'replaceStr': "-debug.js" 454 * } 455 * 456 * @property filter 457 * @type string| {searchExp: string, replaceStr: string} 458 */ 459 // self.filter = null; 460 461 /** 462 * per-component filter specification. If specified for a given 463 * component, this overrides the filter config. 464 * @property filters 465 * @type object 466 */ 467 self.filters = {}; 468 469 /** 470 * The list of requested modules 471 * @property required 472 * @type {string: boolean} 473 */ 474 self.required = {}; 475 476 /** 477 * If a module name is predefined when requested, it is checked againsts 478 * the patterns provided in this property. If there is a match, the 479 * module is added with the default configuration. 480 * 481 * At the moment only supporting module prefixes, but anticipate 482 * supporting at least regular expressions. 483 * @property patterns 484 * @type Object 485 */ 486 // self.patterns = Y.merge(Y.Env.meta.patterns); 487 self.patterns = {}; 488 489 /** 490 * Internal loader instance metadata. Use accessor `getModuleInfo()` instead. 491 */ 492 self.moduleInfo = {}; 493 494 self.groups = Y.merge(Y.Env.meta.groups); 495 496 /** 497 * Provides the information used to skin the skinnable components. 498 * The following skin definition would result in 'skin1' and 'skin2' 499 * being loaded for calendar (if calendar was requested), and 500 * 'sam' for all other skinnable components: 501 * 502 * skin: { 503 * // The default skin, which is automatically applied if not 504 * // overriden by a component-specific skin definition. 505 * // Change this in to apply a different skin globally 506 * defaultSkin: 'sam', 507 * 508 * // This is combined with the loader base property to get 509 * // the default root directory for a skin. ex: 510 * // http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/ 511 * base: 'assets/skins/', 512 * 513 * // Any component-specific overrides can be specified here, 514 * // making it possible to load different skins for different 515 * // components. It is possible to load more than one skin 516 * // for a given component as well. 517 * overrides: { 518 * calendar: ['skin1', 'skin2'] 519 * } 520 * } 521 * @property skin 522 * @type {Object} 523 */ 524 self.skin = Y.merge(Y.Env.meta.skin); 525 526 /* 527 * Map of conditional modules 528 * @since 3.2.0 529 */ 530 self.conditions = {}; 531 532 // map of modules with a hash of modules that meet the requirement 533 // self.provides = {}; 534 535 self.config = o; 536 self._internal = true; 537 538 self._populateConditionsCache(); 539 540 /** 541 * Set when beginning to compute the dependency tree. 542 * Composed of what YUI reports to be loaded combined 543 * with what has been loaded by any instance on the page 544 * with the version number specified in the metadata. 545 * @property loaded 546 * @type {string: boolean} 547 */ 548 self.loaded = GLOBAL_LOADED[VERSION]; 549 550 551 /** 552 * Should Loader fetch scripts in `async`, defaults to `true` 553 * @property async 554 */ 555 556 self.async = true; 557 558 self._inspectPage(); 559 560 self._internal = false; 561 562 self._config(o); 563 564 self.forceMap = (self.force) ? Y.Array.hash(self.force) : {}; 565 566 self.testresults = null; 567 568 if (Y.config.tests) { 569 self.testresults = Y.config.tests; 570 } 571 572 /** 573 * List of rollup files found in the library metadata 574 * @property rollups 575 */ 576 // self.rollups = null; 577 578 /** 579 * Whether or not to load optional dependencies for 580 * the requested modules 581 * @property loadOptional 582 * @type boolean 583 * @default false 584 */ 585 // self.loadOptional = false; 586 587 /** 588 * All of the derived dependencies in sorted order, which 589 * will be populated when either calculate() or insert() 590 * is called 591 * @property sorted 592 * @type string[] 593 */ 594 self.sorted = []; 595 596 /* 597 * A list of modules to attach to the YUI instance when complete. 598 * If not supplied, the sorted list of dependencies are applied. 599 * @property attaching 600 */ 601 // self.attaching = null; 602 603 /** 604 * Flag to indicate the dependency tree needs to be recomputed 605 * if insert is called again. 606 * @property dirty 607 * @type boolean 608 * @default true 609 */ 610 self.dirty = true; 611 612 /** 613 * List of modules inserted by the utility 614 * @property inserted 615 * @type {string: boolean} 616 */ 617 self.inserted = {}; 618 619 /** 620 * List of skipped modules during insert() because the module 621 * was not defined 622 * @property skipped 623 */ 624 self.skipped = {}; 625 626 // Y.on('yui:load', self.loadNext, self); 627 628 self.tested = {}; 629 630 /* 631 * Cached sorted calculate results 632 * @property results 633 * @since 3.2.0 634 */ 635 //self.results = {}; 636 637 if (self.ignoreRegistered) { 638 //Clear inpage already processed modules. 639 self._resetModules(); 640 } 641 642 }; 643 644 Y.Loader.prototype = { 645 /** 646 * Gets the module info from the local moduleInfo hash, or from the 647 * default metadata and populate the local moduleInfo hash. 648 * @method getModuleInfo 649 * @param {string} name of the module 650 * @public 651 */ 652 getModuleInfo: function(name) { 653 654 var m = this.moduleInfo[name], 655 rawMetaModules, globalRenderedMods, internal, v; 656 657 if (m) { 658 return m; 659 } 660 661 rawMetaModules = META.modules; 662 globalRenderedMods = GLOBAL_ENV._renderedMods; 663 internal = this._internal; 664 665 /* 666 The logic here is: 667 668 - if the `moduleInfo[name]` is avilable, 669 then short circuit 670 - otherwise, if the module is in the globalCache (cross Y instance), 671 then port it from the global registry into `moduleInfo[name]` 672 - otherwise, if the module has raw metadata (from meta modules) 673 then add it to the global registry and to `moduleInfo[name]` 674 675 */ 676 if (globalRenderedMods && globalRenderedMods.hasOwnProperty(name) && !this.ignoreRegistered) { 677 this.moduleInfo[name] = Y.merge(globalRenderedMods[name]); 678 } else { 679 if (rawMetaModules.hasOwnProperty(name)) { 680 this._internal = true; // making sure that modules from raw data are marked as internal 681 v = this.addModule(rawMetaModules[name], name); 682 // Inspect the page for the CSS module and mark it as loaded. 683 if (v && v.type === CSS) { 684 if (this.isCSSLoaded(v.name, true)) { 685 this.loaded[v.name] = true; 686 } 687 } 688 this._internal = internal; 689 } 690 } 691 return this.moduleInfo[name]; 692 }, 693 /** 694 * Expand the names that are aliases to other modules. 695 * @method _expandAliases 696 * @param {string[]} list a module name or a list of names to be expanded 697 * @private 698 * @return {array} 699 */ 700 _expandAliases: function(list) { 701 var expanded = [], 702 aliases = YUI.Env.aliases, 703 i, name; 704 list = Y.Array(list); 705 for (i = 0; i < list.length; i += 1) { 706 name = list[i]; 707 expanded.push.apply(expanded, aliases[name] ? aliases[name] : [name]); 708 } 709 return expanded; 710 }, 711 /** 712 * Populate the conditions cache from raw modules, this is necessary 713 * because no other module will require a conditional module, instead 714 * the condition has to be executed and then the module is analyzed 715 * to be included in the final requirement list. Without this cache 716 * conditional modules will be simply ignored. 717 * @method _populateConditionsCache 718 * @private 719 */ 720 _populateConditionsCache: function() { 721 var rawMetaModules = META.modules, 722 cache = GLOBAL_ENV._conditions, 723 i, j, t, trigger; 724 725 // if we have conditions in cache and cache is enabled 726 // we should port them to this loader instance 727 if (cache && !this.ignoreRegistered) { 728 for (i in cache) { 729 if (cache.hasOwnProperty(i)) { 730 this.conditions[i] = Y.merge(cache[i]); 731 } 732 } 733 } else { 734 for (i in rawMetaModules) { 735 if (rawMetaModules.hasOwnProperty(i) && rawMetaModules[i].condition) { 736 t = this._expandAliases(rawMetaModules[i].condition.trigger); 737 for (j = 0; j < t.length; j += 1) { 738 trigger = t[j]; 739 this.conditions[trigger] = this.conditions[trigger] || {}; 740 this.conditions[trigger][rawMetaModules[i].name || i] = rawMetaModules[i].condition; 741 } 742 } 743 } 744 GLOBAL_ENV._conditions = this.conditions; 745 } 746 }, 747 /** 748 * Reset modules in the module cache to a pre-processed state so additional 749 * computations with a different skin or language will work as expected. 750 * @method _resetModules 751 * @private 752 */ 753 _resetModules: function() { 754 var self = this, i, o, 755 mod, name, details; 756 for (i in self.moduleInfo) { 757 if (self.moduleInfo.hasOwnProperty(i) && self.moduleInfo[i]) { 758 mod = self.moduleInfo[i]; 759 name = mod.name; 760 details = (YUI.Env.mods[name] ? YUI.Env.mods[name].details : null); 761 762 if (details) { 763 self.moduleInfo[name]._reset = true; 764 self.moduleInfo[name].requires = details.requires || []; 765 self.moduleInfo[name].optional = details.optional || []; 766 self.moduleInfo[name].supersedes = details.supercedes || []; 767 } 768 769 if (mod.defaults) { 770 for (o in mod.defaults) { 771 if (mod.defaults.hasOwnProperty(o)) { 772 if (mod[o]) { 773 mod[o] = mod.defaults[o]; 774 } 775 } 776 } 777 } 778 mod.langCache = undefined; 779 mod.skinCache = undefined; 780 if (mod.skinnable) { 781 self._addSkin(self.skin.defaultSkin, mod.name); 782 } 783 } 784 } 785 }, 786 /** 787 Regex that matches a CSS URL. Used to guess the file type when it's not 788 specified. 789 790 @property REGEX_CSS 791 @type RegExp 792 @final 793 @protected 794 @since 3.5.0 795 **/ 796 REGEX_CSS: /\.css(?:[?;].*)?$/i, 797 798 /** 799 * Default filters for raw and debug 800 * @property FILTER_DEFS 801 * @type Object 802 * @final 803 * @protected 804 */ 805 FILTER_DEFS: { 806 RAW: { 807 'searchExp': '-min\\.js', 808 'replaceStr': '.js' 809 }, 810 DEBUG: { 811 'searchExp': '-min\\.js', 812 'replaceStr': '-debug.js' 813 }, 814 COVERAGE: { 815 'searchExp': '-min\\.js', 816 'replaceStr': '-coverage.js' 817 } 818 }, 819 /* 820 * Check the pages meta-data and cache the result. 821 * @method _inspectPage 822 * @private 823 */ 824 _inspectPage: function() { 825 var self = this, v, m, req, mr, i; 826 827 for (i in ON_PAGE) { 828 if (ON_PAGE.hasOwnProperty(i)) { 829 v = ON_PAGE[i]; 830 if (v.details) { 831 m = self.getModuleInfo(v.name); 832 req = v.details.requires; 833 mr = m && m.requires; 834 835 if (m) { 836 if (!m._inspected && req && mr.length !== req.length) { 837 // console.log('deleting ' + m.name); 838 delete m.expanded; 839 } 840 } else { 841 m = self.addModule(v.details, i); 842 } 843 m._inspected = true; 844 } 845 } 846 } 847 }, 848 /* 849 * returns true if b is not loaded, and is required directly or by means of modules it supersedes. 850 * @private 851 * @method _requires 852 * @param {String} mod1 The first module to compare 853 * @param {String} mod2 The second module to compare 854 */ 855 _requires: function(mod1, mod2) { 856 857 var i, rm, after_map, s, 858 m = this.getModuleInfo(mod1), 859 other = this.getModuleInfo(mod2); 860 861 if (!m || !other) { 862 return false; 863 } 864 865 rm = m.expanded_map; 866 after_map = m.after_map; 867 868 // check if this module should be sorted after the other 869 // do this first to short circut circular deps 870 if (after_map && (mod2 in after_map)) { 871 return true; 872 } 873 874 after_map = other.after_map; 875 876 // and vis-versa 877 if (after_map && (mod1 in after_map)) { 878 return false; 879 } 880 881 // check if this module requires one the other supersedes 882 s = other.supersedes; 883 if (s) { 884 for (i = 0; i < s.length; i++) { 885 if (this._requires(mod1, s[i])) { 886 return true; 887 } 888 } 889 } 890 891 s = m.supersedes; 892 if (s) { 893 for (i = 0; i < s.length; i++) { 894 if (this._requires(mod2, s[i])) { 895 return false; 896 } 897 } 898 } 899 900 // check if this module requires the other directly 901 // if (r && yArray.indexOf(r, mod2) > -1) { 902 if (rm && (mod2 in rm)) { 903 return true; 904 } 905 906 // external css files should be sorted below yui css 907 if (m.ext && m.type === CSS && !other.ext && other.type === CSS) { 908 return true; 909 } 910 911 return false; 912 }, 913 /** 914 * Apply a new config to the Loader instance 915 * @method _config 916 * @private 917 * @param {Object} o The new configuration 918 */ 919 _config: function(o) { 920 var i, j, val, a, f, group, groupName, self = this, 921 mods = [], mod, modInfo; 922 // apply config values 923 if (o) { 924 for (i in o) { 925 if (o.hasOwnProperty(i)) { 926 val = o[i]; 927 //TODO This should be a case 928 if (i === 'require') { 929 self.require(val); 930 } else if (i === 'skin') { 931 //If the config.skin is a string, format to the expected object 932 if (typeof val === 'string') { 933 self.skin.defaultSkin = o.skin; 934 val = { 935 defaultSkin: val 936 }; 937 } 938 939 Y.mix(self.skin, val, true); 940 } else if (i === 'groups') { 941 for (j in val) { 942 if (val.hasOwnProperty(j)) { 943 groupName = j; 944 group = val[j]; 945 self.addGroup(group, groupName); 946 if (group.aliases) { 947 for (a in group.aliases) { 948 if (group.aliases.hasOwnProperty(a)) { 949 self.addAlias(group.aliases[a], a); 950 } 951 } 952 } 953 } 954 } 955 956 } else if (i === 'modules') { 957 // add a hash of module definitions 958 for (j in val) { 959 if (val.hasOwnProperty(j)) { 960 self.addModule(val[j], j); 961 } 962 } 963 } else if (i === 'aliases') { 964 for (j in val) { 965 if (val.hasOwnProperty(j)) { 966 self.addAlias(val[j], j); 967 } 968 } 969 } else if (i === 'gallery') { 970 if (this.groups.gallery.update) { 971 this.groups.gallery.update(val, o); 972 } 973 } else if (i === 'yui2' || i === '2in3') { 974 if (this.groups.yui2.update) { 975 this.groups.yui2.update(o['2in3'], o.yui2, o); 976 } 977 } else { 978 self[i] = val; 979 } 980 } 981 } 982 } 983 984 // fix filter 985 f = self.filter; 986 987 if (L.isString(f)) { 988 f = f.toUpperCase(); 989 self.filterName = f; 990 self.filter = self.FILTER_DEFS[f]; 991 if (f === 'DEBUG') { 992 self.require('yui-log', 'dump'); 993 } 994 } 995 996 if (self.filterName && self.coverage) { 997 if (self.filterName === 'COVERAGE' && L.isArray(self.coverage) && self.coverage.length) { 998 for (i = 0; i < self.coverage.length; i++) { 999 mod = self.coverage[i]; 1000 modInfo = self.getModuleInfo(mod); 1001 if (modInfo && modInfo.use) { 1002 mods = mods.concat(modInfo.use); 1003 } else { 1004 mods.push(mod); 1005 } 1006 } 1007 self.filters = self.filters || {}; 1008 Y.Array.each(mods, function(mod) { 1009 self.filters[mod] = self.FILTER_DEFS.COVERAGE; 1010 }); 1011 self.filterName = 'RAW'; 1012 self.filter = self.FILTER_DEFS[self.filterName]; 1013 } 1014 } 1015 1016 }, 1017 1018 /** 1019 * Returns the skin module name for the specified skin name. If a 1020 * module name is supplied, the returned skin module name is 1021 * specific to the module passed in. 1022 * @method formatSkin 1023 * @param {string} skin the name of the skin. 1024 * @param {string} mod optional: the name of a module to skin. 1025 * @return {string} the full skin module name. 1026 */ 1027 formatSkin: function(skin, mod) { 1028 var s = SKIN_PREFIX + skin; 1029 if (mod) { 1030 s = s + '-' + mod; 1031 } 1032 1033 return s; 1034 }, 1035 1036 /** 1037 * Adds the skin def to the module info 1038 * @method _addSkin 1039 * @param {string} skin the name of the skin. 1040 * @param {string} mod the name of the module. 1041 * @param {string} parent parent module if this is a skin of a 1042 * submodule or plugin. 1043 * @return {string} the module name for the skin. 1044 * @private 1045 */ 1046 _addSkin: function(skin, mod, parent) { 1047 var pkg, name, nmod, 1048 sinf = this.skin, 1049 mdef = mod && this.getModuleInfo(mod), 1050 ext = mdef && mdef.ext; 1051 1052 // Add a module definition for the module-specific skin css 1053 if (mod) { 1054 name = this.formatSkin(skin, mod); 1055 if (!this.getModuleInfo(name)) { 1056 pkg = mdef.pkg || mod; 1057 nmod = { 1058 skin: true, 1059 name: name, 1060 group: mdef.group, 1061 type: 'css', 1062 after: sinf.after, 1063 path: (parent || pkg) + '/' + sinf.base + skin + 1064 '/' + mod + '.css', 1065 ext: ext 1066 }; 1067 if (mdef.base) { 1068 nmod.base = mdef.base; 1069 } 1070 if (mdef.configFn) { 1071 nmod.configFn = mdef.configFn; 1072 } 1073 this.addModule(nmod, name); 1074 1075 } 1076 } 1077 1078 return name; 1079 }, 1080 /** 1081 * Adds an alias module to the system 1082 * @method addAlias 1083 * @param {Array} use An array of modules that makes up this alias 1084 * @param {String} name The name of the alias 1085 * @example 1086 * var loader = new Y.Loader({}); 1087 * loader.addAlias([ 'node', 'yql' ], 'davglass'); 1088 * loader.require(['davglass']); 1089 * var out = loader.resolve(true); 1090 * 1091 * //out.js will contain Node and YQL modules 1092 */ 1093 addAlias: function(use, name) { 1094 YUI.Env.aliases[name] = use; 1095 this.addModule({ 1096 name: name, 1097 use: use 1098 }); 1099 }, 1100 /** 1101 * Add a new module group 1102 * @method addGroup 1103 * @param {Object} config An object containing the group configuration data 1104 * @param {String} config.name required, the group name 1105 * @param {String} config.base The base directory for this module group 1106 * @param {String} config.root The root path to add to each combo resource path 1107 * @param {Boolean} config.combine Should the request be combined 1108 * @param {String} config.comboBase Combo service base path 1109 * @param {Object} config.modules The group of modules 1110 * @param {String} name the group name. 1111 * @example 1112 * var loader = new Y.Loader({}); 1113 * loader.addGroup({ 1114 * name: 'davglass', 1115 * combine: true, 1116 * comboBase: '/combo?', 1117 * root: '', 1118 * modules: { 1119 * //Module List here 1120 * } 1121 * }, 'davglass'); 1122 */ 1123 addGroup: function(o, name) { 1124 var mods = o.modules, 1125 self = this, i, v; 1126 1127 name = name || o.name; 1128 o.name = name; 1129 self.groups[name] = o; 1130 1131 if (o.patterns) { 1132 for (i in o.patterns) { 1133 if (o.patterns.hasOwnProperty(i)) { 1134 o.patterns[i].group = name; 1135 self.patterns[i] = o.patterns[i]; 1136 } 1137 } 1138 } 1139 1140 if (mods) { 1141 for (i in mods) { 1142 if (mods.hasOwnProperty(i)) { 1143 v = mods[i]; 1144 if (typeof v === 'string') { 1145 v = { name: i, fullpath: v }; 1146 } 1147 v.group = name; 1148 self.addModule(v, i); 1149 } 1150 } 1151 } 1152 }, 1153 1154 /** 1155 * Add a new module to the component metadata. 1156 * @method addModule 1157 * @param {Object} config An object containing the module data. 1158 * @param {String} config.name Required, the component name 1159 * @param {String} config.type Required, the component type (js or css) 1160 * @param {String} config.path Required, the path to the script from `base` 1161 * @param {Array} config.requires Array of modules required by this component 1162 * @param {Array} [config.optional] Array of optional modules for this component 1163 * @param {Array} [config.supersedes] Array of the modules this component replaces 1164 * @param {Array} [config.after] Array of modules the components which, if present, should be sorted above this one 1165 * @param {Object} [config.after_map] Faster alternative to 'after' -- supply a hash instead of an array 1166 * @param {Number} [config.rollup] The number of superseded modules required for automatic rollup 1167 * @param {String} [config.fullpath] If `fullpath` is specified, this is used instead of the configured `base + path` 1168 * @param {Boolean} [config.skinnable] Flag to determine if skin assets should automatically be pulled in 1169 * @param {Object} [config.submodules] Hash of submodules 1170 * @param {String} [config.group] The group the module belongs to -- this is set automatically when it is added as part of a group configuration. 1171 * @param {Array} [config.lang] Array of BCP 47 language tags of languages for which this module has localized resource bundles, e.g., `["en-GB", "zh-Hans-CN"]` 1172 * @param {Object} [config.condition] Specifies that the module should be loaded automatically if a condition is met. This is an object with up to four fields: 1173 * @param {String} [config.condition.trigger] The name of a module that can trigger the auto-load 1174 * @param {Function} [config.condition.test] A function that returns true when the module is to be loaded. 1175 * @param {String} [config.condition.ua] The UA name of <a href="UA.html">Y.UA</a> object that returns true when the module is to be loaded. e.g., `"ie"`, `"nodejs"`. 1176 * @param {String} [config.condition.when] Specifies the load order of the conditional module 1177 * with regard to the position of the trigger module. 1178 * This should be one of three values: `before`, `after`, or `instead`. The default is `after`. 1179 * @param {Object} [config.testresults] A hash of test results from `Y.Features.all()` 1180 * @param {Function} [config.configFn] A function to exectute when configuring this module 1181 * @param {Object} config.configFn.mod The module config, modifying this object will modify it's config. Returning false will delete the module's config. 1182 * @param {String[]} [config.optionalRequires] List of dependencies that 1183 may optionally be loaded by this loader. This is targeted mostly at 1184 polyfills, since they should not be in the list of requires because 1185 polyfills are assumed to be available in the global scope. 1186 * @param {Function} [config.test] Test to be called when this module is 1187 added as an optional dependency of another module. If the test function 1188 returns `false`, the module will be ignored and will not be attached to 1189 this YUI instance. 1190 * @param {String} [name] The module name, required if not in the module data. 1191 * @return {Object} the module definition or null if the object passed in did not provide all required attributes. 1192 */ 1193 addModule: function(o, name) { 1194 name = name || o.name; 1195 1196 if (typeof o === 'string') { 1197 o = { name: name, fullpath: o }; 1198 } 1199 1200 1201 var subs, i, l, t, sup, s, smod, plugins, plug, 1202 j, langs, packName, supName, flatSup, flatLang, lang, ret, 1203 overrides, skinname, when, g, p, 1204 modInfo = this.moduleInfo[name], 1205 conditions = this.conditions, trigger; 1206 1207 //Only merge this data if the temp flag is set 1208 //from an earlier pass from a pattern or else 1209 //an override module (YUI_config) can not be used to 1210 //replace a default module. 1211 if (modInfo && modInfo.temp) { 1212 //This catches temp modules loaded via a pattern 1213 // The module will be added twice, once from the pattern and 1214 // Once from the actual add call, this ensures that properties 1215 // that were added to the module the first time around (group: gallery) 1216 // are also added the second time around too. 1217 o = Y.merge(modInfo, o); 1218 } 1219 1220 o.name = name; 1221 1222 if (!o || !o.name) { 1223 return null; 1224 } 1225 1226 if (!o.type) { 1227 //Always assume it's javascript unless the CSS pattern is matched. 1228 o.type = JS; 1229 p = o.path || o.fullpath; 1230 if (p && this.REGEX_CSS.test(p)) { 1231 o.type = CSS; 1232 } 1233 } 1234 1235 if (!o.path && !o.fullpath) { 1236 o.path = _path(name, name, o.type); 1237 } 1238 o.supersedes = o.supersedes || o.use; 1239 1240 o.ext = ('ext' in o) ? o.ext : (this._internal) ? false : true; 1241 1242 // Handle submodule logic 1243 subs = o.submodules; 1244 1245 this.moduleInfo[name] = o; 1246 1247 o.requires = o.requires || []; 1248 1249 /* 1250 Only allowing the cascade of requires information, since 1251 optional and supersedes are far more fine grained than 1252 a blanket requires is. 1253 */ 1254 if (this.requires) { 1255 for (i = 0; i < this.requires.length; i++) { 1256 o.requires.push(this.requires[i]); 1257 } 1258 } 1259 if (o.group && this.groups && this.groups[o.group]) { 1260 g = this.groups[o.group]; 1261 if (g.requires) { 1262 for (i = 0; i < g.requires.length; i++) { 1263 o.requires.push(g.requires[i]); 1264 } 1265 } 1266 } 1267 1268 1269 if (!o.defaults) { 1270 o.defaults = { 1271 requires: o.requires ? [].concat(o.requires) : null, 1272 supersedes: o.supersedes ? [].concat(o.supersedes) : null, 1273 optional: o.optional ? [].concat(o.optional) : null 1274 }; 1275 } 1276 1277 if (o.skinnable && o.ext && o.temp) { 1278 skinname = this._addSkin(this.skin.defaultSkin, name); 1279 o.requires.unshift(skinname); 1280 } 1281 1282 if (o.requires.length) { 1283 o.requires = this.filterRequires(o.requires) || []; 1284 } 1285 1286 if (!o.langPack && o.lang) { 1287 langs = yArray(o.lang); 1288 for (j = 0; j < langs.length; j++) { 1289 lang = langs[j]; 1290 packName = this.getLangPackName(lang, name); 1291 smod = this.getModuleInfo(packName); 1292 if (!smod) { 1293 smod = this._addLangPack(lang, o, packName); 1294 } 1295 } 1296 } 1297 1298 1299 if (subs) { 1300 sup = o.supersedes || []; 1301 l = 0; 1302 1303 for (i in subs) { 1304 if (subs.hasOwnProperty(i)) { 1305 s = subs[i]; 1306 1307 s.path = s.path || _path(name, i, o.type); 1308 s.pkg = name; 1309 s.group = o.group; 1310 1311 if (s.supersedes) { 1312 sup = sup.concat(s.supersedes); 1313 } 1314 1315 smod = this.addModule(s, i); 1316 sup.push(i); 1317 1318 if (smod.skinnable) { 1319 o.skinnable = true; 1320 overrides = this.skin.overrides; 1321 if (overrides && overrides[i]) { 1322 for (j = 0; j < overrides[i].length; j++) { 1323 skinname = this._addSkin(overrides[i][j], 1324 i, name); 1325 sup.push(skinname); 1326 } 1327 } 1328 skinname = this._addSkin(this.skin.defaultSkin, 1329 i, name); 1330 sup.push(skinname); 1331 } 1332 1333 // looks like we are expected to work out the metadata 1334 // for the parent module language packs from what is 1335 // specified in the child modules. 1336 if (s.lang && s.lang.length) { 1337 1338 langs = yArray(s.lang); 1339 for (j = 0; j < langs.length; j++) { 1340 lang = langs[j]; 1341 packName = this.getLangPackName(lang, name); 1342 supName = this.getLangPackName(lang, i); 1343 smod = this.getModuleInfo(packName); 1344 1345 if (!smod) { 1346 smod = this._addLangPack(lang, o, packName); 1347 } 1348 1349 flatSup = flatSup || yArray.hash(smod.supersedes); 1350 1351 if (!(supName in flatSup)) { 1352 smod.supersedes.push(supName); 1353 } 1354 1355 o.lang = o.lang || []; 1356 1357 flatLang = flatLang || yArray.hash(o.lang); 1358 1359 if (!(lang in flatLang)) { 1360 o.lang.push(lang); 1361 } 1362 1363 // Add rollup file, need to add to supersedes list too 1364 1365 // default packages 1366 packName = this.getLangPackName(ROOT_LANG, name); 1367 supName = this.getLangPackName(ROOT_LANG, i); 1368 1369 smod = this.getModuleInfo(packName); 1370 1371 if (!smod) { 1372 smod = this._addLangPack(lang, o, packName); 1373 } 1374 1375 if (!(supName in flatSup)) { 1376 smod.supersedes.push(supName); 1377 } 1378 1379 // Add rollup file, need to add to supersedes list too 1380 1381 } 1382 } 1383 1384 l++; 1385 } 1386 } 1387 //o.supersedes = YObject.keys(yArray.hash(sup)); 1388 o.supersedes = yArray.dedupe(sup); 1389 if (this.allowRollup) { 1390 o.rollup = (l < 4) ? l : Math.min(l - 1, 4); 1391 } 1392 } 1393 1394 plugins = o.plugins; 1395 if (plugins) { 1396 for (i in plugins) { 1397 if (plugins.hasOwnProperty(i)) { 1398 plug = plugins[i]; 1399 plug.pkg = name; 1400 plug.path = plug.path || _path(name, i, o.type); 1401 plug.requires = plug.requires || []; 1402 plug.group = o.group; 1403 this.addModule(plug, i); 1404 if (o.skinnable) { 1405 this._addSkin(this.skin.defaultSkin, i, name); 1406 } 1407 1408 } 1409 } 1410 } 1411 1412 if (o.condition) { 1413 t = this._expandAliases(o.condition.trigger); 1414 for (i = 0; i < t.length; i++) { 1415 trigger = t[i]; 1416 when = o.condition.when; 1417 conditions[trigger] = conditions[trigger] || {}; 1418 conditions[trigger][name] = o.condition; 1419 // the 'when' attribute can be 'before', 'after', or 'instead' 1420 // the default is after. 1421 if (when && when !== 'after') { 1422 if (when === 'instead') { // replace the trigger 1423 o.supersedes = o.supersedes || []; 1424 o.supersedes.push(trigger); 1425 } 1426 // before the trigger 1427 // the trigger requires the conditional mod, 1428 // so it should appear before the conditional 1429 // mod if we do not intersede. 1430 } else { // after the trigger 1431 o.after = o.after || []; 1432 o.after.push(trigger); 1433 } 1434 } 1435 } 1436 1437 if (o.supersedes) { 1438 o.supersedes = this.filterRequires(o.supersedes); 1439 } 1440 1441 if (o.after) { 1442 o.after = this.filterRequires(o.after); 1443 o.after_map = yArray.hash(o.after); 1444 } 1445 1446 // this.dirty = true; 1447 1448 if (o.configFn) { 1449 ret = o.configFn(o); 1450 if (ret === false) { 1451 delete this.moduleInfo[name]; 1452 delete GLOBAL_ENV._renderedMods[name]; 1453 o = null; 1454 } 1455 } 1456 //Add to global cache 1457 if (o) { 1458 if (!GLOBAL_ENV._renderedMods) { 1459 GLOBAL_ENV._renderedMods = {}; 1460 } 1461 GLOBAL_ENV._renderedMods[name] = Y.mix(GLOBAL_ENV._renderedMods[name] || {}, o); 1462 GLOBAL_ENV._conditions = conditions; 1463 } 1464 1465 return o; 1466 }, 1467 1468 /** 1469 * Add a requirement for one or more module 1470 * @method require 1471 * @param {string[] | string*} what the modules to load. 1472 */ 1473 require: function(what) { 1474 var a = (typeof what === 'string') ? yArray(arguments) : what; 1475 this.dirty = true; 1476 this.required = Y.merge(this.required, yArray.hash(this.filterRequires(a))); 1477 1478 this._explodeRollups(); 1479 }, 1480 /** 1481 * Grab all the items that were asked for, check to see if the Loader 1482 * meta-data contains a "use" array. If it doesm remove the asked item and replace it with 1483 * the content of the "use". 1484 * This will make asking for: "dd" 1485 * Actually ask for: "dd-ddm-base,dd-ddm,dd-ddm-drop,dd-drag,dd-proxy,dd-constrain,dd-drop,dd-scroll,dd-drop-plugin" 1486 * @private 1487 * @method _explodeRollups 1488 */ 1489 _explodeRollups: function() { 1490 var self = this, m, m2, i, a, v, len, len2, 1491 r = self.required; 1492 1493 if (!self.allowRollup) { 1494 for (i in r) { 1495 if (r.hasOwnProperty(i)) { 1496 m = self.getModule(i); 1497 if (m && m.use) { 1498 len = m.use.length; 1499 for (a = 0; a < len; a++) { 1500 m2 = self.getModule(m.use[a]); 1501 if (m2 && m2.use) { 1502 len2 = m2.use.length; 1503 for (v = 0; v < len2; v++) { 1504 r[m2.use[v]] = true; 1505 } 1506 } else { 1507 r[m.use[a]] = true; 1508 } 1509 } 1510 } 1511 } 1512 } 1513 self.required = r; 1514 } 1515 1516 }, 1517 /** 1518 * Explodes the required array to remove aliases and replace them with real modules 1519 * @method filterRequires 1520 * @param {Array} r The original requires array 1521 * @return {Array} The new array of exploded requirements 1522 */ 1523 filterRequires: function(r) { 1524 if (r) { 1525 if (!Y.Lang.isArray(r)) { 1526 r = [r]; 1527 } 1528 r = Y.Array(r); 1529 var c = [], i, mod, o, m; 1530 1531 for (i = 0; i < r.length; i++) { 1532 mod = this.getModule(r[i]); 1533 if (mod && mod.use) { 1534 for (o = 0; o < mod.use.length; o++) { 1535 //Must walk the other modules in case a module is a rollup of rollups (datatype) 1536 m = this.getModule(mod.use[o]); 1537 if (m && m.use && (m.name !== mod.name)) { 1538 c = Y.Array.dedupe([].concat(c, this.filterRequires(m.use))); 1539 } else { 1540 c.push(mod.use[o]); 1541 } 1542 } 1543 } else { 1544 c.push(r[i]); 1545 } 1546 } 1547 r = c; 1548 } 1549 return r; 1550 }, 1551 1552 /** 1553 Returns `true` if the module can be attached to the YUI instance. Runs 1554 the module's test if there is one and caches its result. 1555 1556 @method _canBeAttached 1557 @param {String} module Name of the module to check. 1558 @return {Boolean} Result of the module's test if it has one, or `true`. 1559 **/ 1560 _canBeAttached: function (m) { 1561 m = this.getModule(m); 1562 if (m && m.test) { 1563 if (!m.hasOwnProperty('_testResult')) { 1564 m._testResult = m.test(Y); 1565 } 1566 return m._testResult; 1567 } 1568 // return `true` for modules not registered as Loader will know what 1569 // to do with them later on 1570 return true; 1571 }, 1572 1573 /** 1574 * Returns an object containing properties for all modules required 1575 * in order to load the requested module 1576 * @method getRequires 1577 * @param {object} mod The module definition from moduleInfo. 1578 * @return {array} the expanded requirement list. 1579 */ 1580 getRequires: function(mod) { 1581 1582 if (!mod) { 1583 //console.log('returning no reqs for ' + mod.name); 1584 return NO_REQUIREMENTS; 1585 } 1586 1587 if (mod._parsed) { 1588 //console.log('returning requires for ' + mod.name, mod.requires); 1589 return mod.expanded || NO_REQUIREMENTS; 1590 } 1591 1592 //TODO add modue cache here out of scope.. 1593 1594 var i, m, j, length, add, packName, lang, testresults = this.testresults, 1595 name = mod.name, cond, 1596 adddef = ON_PAGE[name] && ON_PAGE[name].details, 1597 optReqs = mod.optionalRequires, 1598 d, go, def, 1599 r, old_mod, 1600 o, skinmod, skindef, skinpar, skinname, 1601 intl = mod.lang || mod.intl, 1602 ftests = Y.Features && Y.Features.tests.load, 1603 hash, reparse; 1604 1605 // console.log(name); 1606 1607 // pattern match leaves module stub that needs to be filled out 1608 if (mod.temp && adddef) { 1609 old_mod = mod; 1610 mod = this.addModule(adddef, name); 1611 mod.group = old_mod.group; 1612 mod.pkg = old_mod.pkg; 1613 delete mod.expanded; 1614 } 1615 1616 // console.log('cache: ' + mod.langCache + ' == ' + this.lang); 1617 1618 //If a skin or a lang is different, reparse.. 1619 reparse = !((!this.lang || mod.langCache === this.lang) && (mod.skinCache === this.skin.defaultSkin)); 1620 1621 if (mod.expanded && !reparse) { 1622 return mod.expanded; 1623 } 1624 1625 // Optional dependencies are dependencies that may or may not be 1626 // available. 1627 // This feature was designed specifically to be used when transpiling 1628 // ES6 modules, in order to use polyfills and regular scripts that define 1629 // global variables without having to import them since they should be 1630 // available in the global scope. 1631 if (optReqs) { 1632 for (i = 0, length = optReqs.length; i < length; i++) { 1633 if (this._canBeAttached(optReqs[i])) { 1634 mod.requires.push(optReqs[i]); 1635 } 1636 } 1637 } 1638 1639 d = []; 1640 hash = {}; 1641 r = this.filterRequires(mod.requires); 1642 if (mod.lang) { 1643 //If a module has a lang attribute, auto add the intl requirement. 1644 d.unshift('intl'); 1645 r.unshift('intl'); 1646 intl = true; 1647 } 1648 o = this.filterRequires(mod.optional); 1649 1650 1651 mod._parsed = true; 1652 mod.langCache = this.lang; 1653 mod.skinCache = this.skin.defaultSkin; 1654 1655 for (i = 0; i < r.length; i++) { 1656 if (!hash[r[i]]) { 1657 d.push(r[i]); 1658 hash[r[i]] = true; 1659 m = this.getModule(r[i]); 1660 if (m) { 1661 add = this.getRequires(m); 1662 intl = intl || (m.expanded_map && 1663 (INTL in m.expanded_map)); 1664 for (j = 0; j < add.length; j++) { 1665 d.push(add[j]); 1666 } 1667 } 1668 } 1669 } 1670 1671 // get the requirements from superseded modules, if any 1672 r = this.filterRequires(mod.supersedes); 1673 if (r) { 1674 for (i = 0; i < r.length; i++) { 1675 if (!hash[r[i]]) { 1676 // if this module has submodules, the requirements list is 1677 // expanded to include the submodules. This is so we can 1678 // prevent dups when a submodule is already loaded and the 1679 // parent is requested. 1680 if (mod.submodules) { 1681 d.push(r[i]); 1682 } 1683 1684 hash[r[i]] = true; 1685 m = this.getModule(r[i]); 1686 1687 if (m) { 1688 add = this.getRequires(m); 1689 intl = intl || (m.expanded_map && 1690 (INTL in m.expanded_map)); 1691 for (j = 0; j < add.length; j++) { 1692 d.push(add[j]); 1693 } 1694 } 1695 } 1696 } 1697 } 1698 1699 if (o && this.loadOptional) { 1700 for (i = 0; i < o.length; i++) { 1701 if (!hash[o[i]]) { 1702 d.push(o[i]); 1703 hash[o[i]] = true; 1704 m = this.getModuleInfo(o[i]); 1705 if (m) { 1706 add = this.getRequires(m); 1707 intl = intl || (m.expanded_map && 1708 (INTL in m.expanded_map)); 1709 for (j = 0; j < add.length; j++) { 1710 d.push(add[j]); 1711 } 1712 } 1713 } 1714 } 1715 } 1716 1717 cond = this.conditions[name]; 1718 1719 if (cond) { 1720 //Set the module to not parsed since we have conditionals and this could change the dependency tree. 1721 mod._parsed = false; 1722 if (testresults && ftests) { 1723 oeach(testresults, function(result, id) { 1724 var condmod = ftests[id].name; 1725 if (!hash[condmod] && ftests[id].trigger === name) { 1726 if (result && ftests[id]) { 1727 hash[condmod] = true; 1728 d.push(condmod); 1729 } 1730 } 1731 }); 1732 } else { 1733 for (i in cond) { 1734 if (cond.hasOwnProperty(i)) { 1735 if (!hash[i]) { 1736 def = cond[i]; 1737 //first see if they've specfied a ua check 1738 //then see if they've got a test fn & if it returns true 1739 //otherwise just having a condition block is enough 1740 go = def && ((!def.ua && !def.test) || (def.ua && Y.UA[def.ua]) || 1741 (def.test && def.test(Y, r))); 1742 1743 if (go) { 1744 hash[i] = true; 1745 d.push(i); 1746 m = this.getModule(i); 1747 if (m) { 1748 add = this.getRequires(m); 1749 for (j = 0; j < add.length; j++) { 1750 d.push(add[j]); 1751 } 1752 1753 } 1754 } 1755 } 1756 } 1757 } 1758 } 1759 } 1760 1761 // Create skin modules 1762 if (mod.skinnable) { 1763 skindef = this.skin.overrides; 1764 for (i in YUI.Env.aliases) { 1765 if (YUI.Env.aliases.hasOwnProperty(i)) { 1766 if (Y.Array.indexOf(YUI.Env.aliases[i], name) > -1) { 1767 skinpar = i; 1768 } 1769 } 1770 } 1771 if (skindef && (skindef[name] || (skinpar && skindef[skinpar]))) { 1772 skinname = name; 1773 if (skindef[skinpar]) { 1774 skinname = skinpar; 1775 } 1776 for (i = 0; i < skindef[skinname].length; i++) { 1777 skinmod = this._addSkin(skindef[skinname][i], name); 1778 if (!this.isCSSLoaded(skinmod, this._boot)) { 1779 d.push(skinmod); 1780 } 1781 } 1782 } else { 1783 skinmod = this._addSkin(this.skin.defaultSkin, name); 1784 if (!this.isCSSLoaded(skinmod, this._boot)) { 1785 d.push(skinmod); 1786 } 1787 } 1788 } 1789 1790 mod._parsed = false; 1791 1792 if (intl) { 1793 1794 if (mod.lang && !mod.langPack && Y.Intl) { 1795 lang = Y.Intl.lookupBestLang(this.lang || ROOT_LANG, mod.lang); 1796 packName = this.getLangPackName(lang, name); 1797 if (packName) { 1798 d.unshift(packName); 1799 } 1800 } 1801 d.unshift(INTL); 1802 } 1803 1804 mod.expanded_map = yArray.hash(d); 1805 1806 mod.expanded = YObject.keys(mod.expanded_map); 1807 1808 return mod.expanded; 1809 }, 1810 /** 1811 * Check to see if named css module is already loaded on the page 1812 * @method isCSSLoaded 1813 * @param {String} name The name of the css file 1814 * @param {Boolean} skip To skip the short-circuit for ignoreRegister 1815 * @return Boolean 1816 */ 1817 isCSSLoaded: function(name, skip) { 1818 //TODO - Make this call a batching call with name being an array 1819 if (!name || !YUI.Env.cssStampEl || (!skip && this.ignoreRegistered)) { 1820 return false; 1821 } 1822 var el = YUI.Env.cssStampEl, 1823 ret = false, 1824 mod = YUI.Env._cssLoaded[name], 1825 style = el.currentStyle; //IE 1826 1827 1828 if (mod !== undefined) { 1829 return mod; 1830 } 1831 1832 //Add the classname to the element 1833 el.className = name; 1834 1835 if (!style) { 1836 style = Y.config.doc.defaultView.getComputedStyle(el, null); 1837 } 1838 1839 if (style && style.display === 'none') { 1840 ret = true; 1841 } 1842 1843 1844 el.className = ''; //Reset the classname to '' 1845 1846 YUI.Env._cssLoaded[name] = ret; 1847 1848 return ret; 1849 }, 1850 1851 /** 1852 * Returns a hash of module names the supplied module satisfies. 1853 * @method getProvides 1854 * @param {string} name The name of the module. 1855 * @return {object} what this module provides. 1856 */ 1857 getProvides: function(name) { 1858 var m = this.getModule(name), o, s; 1859 // supmap = this.provides; 1860 1861 if (!m) { 1862 return NOT_FOUND; 1863 } 1864 1865 if (m && !m.provides) { 1866 o = {}; 1867 s = m.supersedes; 1868 1869 if (s) { 1870 yArray.each(s, function(v) { 1871 Y.mix(o, this.getProvides(v)); 1872 }, this); 1873 } 1874 1875 o[name] = true; 1876 m.provides = o; 1877 1878 } 1879 1880 return m.provides; 1881 }, 1882 1883 /** 1884 * Calculates the dependency tree, the result is stored in the sorted 1885 * property. 1886 * @method calculate 1887 * @param {object} o optional options object. 1888 * @param {string} type optional argument to prune modules. 1889 */ 1890 calculate: function(o, type) { 1891 if (o || type || this.dirty) { 1892 1893 if (o) { 1894 this._config(o); 1895 } 1896 1897 if (!this._init) { 1898 this._setup(); 1899 } 1900 1901 this._explode(); 1902 1903 if (this.allowRollup) { 1904 this._rollup(); 1905 } else { 1906 this._explodeRollups(); 1907 } 1908 this._reduce(); 1909 this._sort(); 1910 } 1911 }, 1912 /** 1913 * Creates a "psuedo" package for languages provided in the lang array 1914 * @method _addLangPack 1915 * @private 1916 * @param {String} lang The language to create 1917 * @param {Object} m The module definition to create the language pack around 1918 * @param {String} packName The name of the package (e.g: lang/datatype-date-en-US) 1919 * @return {Object} The module definition 1920 */ 1921 _addLangPack: function(lang, m, packName) { 1922 var name = m.name, 1923 packPath, conf, 1924 existing = this.getModuleInfo(packName); 1925 1926 if (!existing) { 1927 1928 packPath = _path((m.pkg || name), packName, JS, true); 1929 1930 conf = { 1931 path: packPath, 1932 intl: true, 1933 langPack: true, 1934 ext: m.ext, 1935 group: m.group, 1936 supersedes: [] 1937 }; 1938 if (m.root) { 1939 conf.root = m.root; 1940 } 1941 if (m.base) { 1942 conf.base = m.base; 1943 } 1944 1945 if (m.configFn) { 1946 conf.configFn = m.configFn; 1947 } 1948 1949 this.addModule(conf, packName); 1950 1951 if (lang) { 1952 Y.Env.lang = Y.Env.lang || {}; 1953 Y.Env.lang[lang] = Y.Env.lang[lang] || {}; 1954 Y.Env.lang[lang][name] = true; 1955 } 1956 } 1957 1958 return this.getModuleInfo(packName); 1959 }, 1960 1961 /** 1962 * Investigates the current YUI configuration on the page. By default, 1963 * modules already detected will not be loaded again unless a force 1964 * option is encountered. Called by calculate() 1965 * @method _setup 1966 * @private 1967 */ 1968 _setup: function() { 1969 var info = this.moduleInfo, name, i, j, m, l, 1970 packName; 1971 1972 for (name in info) { 1973 if (info.hasOwnProperty(name)) { 1974 m = info[name]; 1975 if (m) { 1976 1977 // remove dups 1978 //m.requires = YObject.keys(yArray.hash(m.requires)); 1979 m.requires = yArray.dedupe(m.requires); 1980 1981 // Create lang pack modules 1982 //if (m.lang && m.lang.length) { 1983 if (m.lang) { 1984 // Setup root package if the module has lang defined, 1985 // it needs to provide a root language pack 1986 packName = this.getLangPackName(ROOT_LANG, name); 1987 this._addLangPack(null, m, packName); 1988 } 1989 1990 } 1991 } 1992 } 1993 1994 1995 //l = Y.merge(this.inserted); 1996 l = {}; 1997 1998 // available modules 1999 if (!this.ignoreRegistered) { 2000 Y.mix(l, GLOBAL_ENV.mods); 2001 } 2002 2003 // add the ignore list to the list of loaded packages 2004 if (this.ignore) { 2005 Y.mix(l, yArray.hash(this.ignore)); 2006 } 2007 2008 // expand the list to include superseded modules 2009 for (j in l) { 2010 if (l.hasOwnProperty(j)) { 2011 Y.mix(l, this.getProvides(j)); 2012 } 2013 } 2014 2015 // remove modules on the force list from the loaded list 2016 if (this.force) { 2017 for (i = 0; i < this.force.length; i++) { 2018 if (this.force[i] in l) { 2019 delete l[this.force[i]]; 2020 } 2021 } 2022 } 2023 2024 Y.mix(this.loaded, l); 2025 2026 this._init = true; 2027 }, 2028 2029 /** 2030 * Builds a module name for a language pack 2031 * @method getLangPackName 2032 * @param {string} lang the language code. 2033 * @param {string} mname the module to build it for. 2034 * @return {string} the language pack module name. 2035 */ 2036 getLangPackName: function(lang, mname) { 2037 return ('lang/' + mname + ((lang) ? '_' + lang : '')); 2038 }, 2039 /** 2040 * Inspects the required modules list looking for additional 2041 * dependencies. Expands the required list to include all 2042 * required modules. Called by calculate() 2043 * @method _explode 2044 * @private 2045 */ 2046 _explode: function() { 2047 //TODO Move done out of scope 2048 var r = this.required, m, reqs, done = {}, 2049 self = this, name, expound; 2050 2051 // the setup phase is over, all modules have been created 2052 self.dirty = false; 2053 2054 self._explodeRollups(); 2055 r = self.required; 2056 2057 for (name in r) { 2058 if (r.hasOwnProperty(name)) { 2059 if (!done[name]) { 2060 done[name] = true; 2061 m = self.getModule(name); 2062 if (m) { 2063 expound = m.expound; 2064 2065 if (expound) { 2066 r[expound] = self.getModule(expound); 2067 reqs = self.getRequires(r[expound]); 2068 Y.mix(r, yArray.hash(reqs)); 2069 } 2070 2071 reqs = self.getRequires(m); 2072 Y.mix(r, yArray.hash(reqs)); 2073 } 2074 } 2075 } 2076 } 2077 2078 }, 2079 /** 2080 * The default method used to test a module against a pattern 2081 * @method _patternTest 2082 * @private 2083 * @param {String} mname The module being tested 2084 * @param {String} pname The pattern to match 2085 */ 2086 _patternTest: function(mname, pname) { 2087 return (mname.indexOf(pname) > -1); 2088 }, 2089 /** 2090 * Get's the loader meta data for the requested module 2091 * @method getModule 2092 * @param {String} mname The module name to get 2093 * @return {Object} The module metadata 2094 */ 2095 getModule: function(mname) { 2096 //TODO: Remove name check - it's a quick hack to fix pattern WIP 2097 if (!mname) { 2098 return null; 2099 } 2100 2101 var p, found, pname, 2102 m = this.getModuleInfo(mname), 2103 patterns = this.patterns; 2104 2105 // check the patterns library to see if we should automatically add 2106 // the module with defaults 2107 if (!m || (m && m.ext)) { 2108 for (pname in patterns) { 2109 if (patterns.hasOwnProperty(pname)) { 2110 p = patterns[pname]; 2111 2112 //There is no test method, create a default one that tests 2113 // the pattern against the mod name 2114 if (!p.test) { 2115 p.test = this._patternTest; 2116 } 2117 2118 if (p.test(mname, pname)) { 2119 // use the metadata supplied for the pattern 2120 // as the module definition. 2121 found = p; 2122 break; 2123 } 2124 } 2125 } 2126 } 2127 2128 if (!m) { 2129 if (found) { 2130 if (p.action) { 2131 p.action.call(this, mname, pname); 2132 } else { 2133 // ext true or false? 2134 m = this.addModule(Y.merge(found, { 2135 test: void 0, 2136 temp: true 2137 }), mname); 2138 if (found.configFn) { 2139 m.configFn = found.configFn; 2140 } 2141 } 2142 } 2143 } else { 2144 if (found && m && found.configFn && !m.configFn) { 2145 m.configFn = found.configFn; 2146 m.configFn(m); 2147 } 2148 } 2149 2150 return m; 2151 }, 2152 2153 // impl in rollup submodule 2154 _rollup: function() { }, 2155 2156 /** 2157 * Remove superceded modules and loaded modules. Called by 2158 * calculate() after we have the mega list of all dependencies 2159 * @method _reduce 2160 * @return {object} the reduced dependency hash. 2161 * @private 2162 */ 2163 _reduce: function(r) { 2164 2165 r = r || this.required; 2166 2167 var i, j, s, m, type = this.loadType, 2168 ignore = this.ignore ? yArray.hash(this.ignore) : false; 2169 2170 for (i in r) { 2171 if (r.hasOwnProperty(i)) { 2172 m = this.getModule(i); 2173 // remove if already loaded 2174 if (((this.loaded[i] || ON_PAGE[i]) && 2175 !this.forceMap[i] && !this.ignoreRegistered) || 2176 (type && m && m.type !== type)) { 2177 delete r[i]; 2178 } 2179 if (ignore && ignore[i]) { 2180 delete r[i]; 2181 } 2182 // remove anything this module supersedes 2183 s = m && m.supersedes; 2184 if (s) { 2185 for (j = 0; j < s.length; j++) { 2186 if (s[j] in r) { 2187 delete r[s[j]]; 2188 } 2189 } 2190 } 2191 } 2192 } 2193 2194 return r; 2195 }, 2196 /** 2197 * Handles the queue when a module has been loaded for all cases 2198 * @method _finish 2199 * @private 2200 * @param {String} msg The message from Loader 2201 * @param {Boolean} success A boolean denoting success or failure 2202 */ 2203 _finish: function(msg, success) { 2204 2205 _queue.running = false; 2206 2207 var onEnd = this.onEnd; 2208 if (onEnd) { 2209 onEnd.call(this.context, { 2210 msg: msg, 2211 data: this.data, 2212 success: success 2213 }); 2214 } 2215 this._continue(); 2216 }, 2217 /** 2218 * The default Loader onSuccess handler, calls this.onSuccess with a payload 2219 * @method _onSuccess 2220 * @private 2221 */ 2222 _onSuccess: function() { 2223 var self = this, skipped = Y.merge(self.skipped), fn, 2224 failed = [], rreg = self.requireRegistration, 2225 success, msg, i, mod; 2226 2227 for (i in skipped) { 2228 if (skipped.hasOwnProperty(i)) { 2229 delete self.inserted[i]; 2230 } 2231 } 2232 2233 self.skipped = {}; 2234 2235 for (i in self.inserted) { 2236 if (self.inserted.hasOwnProperty(i)) { 2237 mod = self.getModule(i); 2238 if (mod && rreg && mod.type === JS && !(i in YUI.Env.mods)) { 2239 failed.push(i); 2240 } else { 2241 Y.mix(self.loaded, self.getProvides(i)); 2242 } 2243 } 2244 } 2245 2246 fn = self.onSuccess; 2247 msg = (failed.length) ? 'notregistered' : 'success'; 2248 success = !(failed.length); 2249 if (fn) { 2250 fn.call(self.context, { 2251 msg: msg, 2252 data: self.data, 2253 success: success, 2254 failed: failed, 2255 skipped: skipped 2256 }); 2257 } 2258 self._finish(msg, success); 2259 }, 2260 /** 2261 * The default Loader onProgress handler, calls this.onProgress with a payload 2262 * @method _onProgress 2263 * @private 2264 */ 2265 _onProgress: function(e) { 2266 var self = this, i; 2267 //set the internal cache to what just came in. 2268 if (e.data && e.data.length) { 2269 for (i = 0; i < e.data.length; i++) { 2270 e.data[i] = self.getModule(e.data[i].name); 2271 } 2272 } 2273 if (self.onProgress) { 2274 self.onProgress.call(self.context, { 2275 name: e.url, 2276 data: e.data 2277 }); 2278 } 2279 }, 2280 /** 2281 * The default Loader onFailure handler, calls this.onFailure with a payload 2282 * @method _onFailure 2283 * @private 2284 */ 2285 _onFailure: function(o) { 2286 var f = this.onFailure, msg = [], i = 0, len = o.errors.length; 2287 2288 for (i; i < len; i++) { 2289 msg.push(o.errors[i].error); 2290 } 2291 2292 msg = msg.join(','); 2293 2294 2295 if (f) { 2296 f.call(this.context, { 2297 msg: msg, 2298 data: this.data, 2299 success: false 2300 }); 2301 } 2302 2303 this._finish(msg, false); 2304 2305 }, 2306 2307 /** 2308 * The default Loader onTimeout handler, calls this.onTimeout with a payload 2309 * @method _onTimeout 2310 * @param {Get.Transaction} transaction The Transaction object from `Y.Get` 2311 * @private 2312 */ 2313 _onTimeout: function(transaction) { 2314 var f = this.onTimeout; 2315 if (f) { 2316 f.call(this.context, { 2317 msg: 'timeout', 2318 data: this.data, 2319 success: false, 2320 transaction: transaction 2321 }); 2322 } 2323 }, 2324 2325 /** 2326 * Sorts the dependency tree. The last step of calculate() 2327 * @method _sort 2328 * @private 2329 */ 2330 _sort: function() { 2331 var name, 2332 2333 // Object containing module names. 2334 required = this.required, 2335 2336 // Keep track of whether we've visited a module. 2337 visited = {}; 2338 2339 // Will contain modules names, in the correct order, 2340 // according to dependencies. 2341 this.sorted = []; 2342 2343 for (name in required) { 2344 if (!visited[name] && required.hasOwnProperty(name)) { 2345 this._visit(name, visited); 2346 } 2347 } 2348 }, 2349 2350 /** 2351 * Recursively visits the dependencies of the module name 2352 * passed in, and appends each module name to the `sorted` property. 2353 * @param {String} name The name of a module. 2354 * @param {Object} visited Keeps track of whether a module was visited. 2355 * @method _visit 2356 * @private 2357 */ 2358 _visit: function (name, visited) { 2359 var required, condition, moduleInfo, dependency, dependencies, 2360 trigger, isAfter, i, l; 2361 2362 visited[name] = true; 2363 required = this.required; 2364 moduleInfo = this.moduleInfo[name]; 2365 condition = this.conditions[name] || {}; 2366 2367 if (moduleInfo) { 2368 // Recurse on each dependency of this module, 2369 // figuring out its dependencies, and so on. 2370 dependencies = moduleInfo.expanded || moduleInfo.requires; 2371 2372 for (i = 0, l = dependencies.length; i < l; ++i) { 2373 dependency = dependencies[i]; 2374 trigger = condition[dependency]; 2375 2376 // We cannot process this dependency yet if it must 2377 // appear after our current module. 2378 isAfter = trigger && (!trigger.when || trigger.when === "after"); 2379 2380 // Is this module name in the required list of modules, 2381 // and have we not already visited it? 2382 if (required[dependency] && !visited[dependency] && !isAfter) { 2383 this._visit(dependency, visited); 2384 } 2385 } 2386 } 2387 2388 this.sorted.push(name); 2389 }, 2390 2391 /** 2392 * Handles the actual insertion of script/link tags 2393 * @method _insert 2394 * @private 2395 * @param {Object} source The YUI instance the request came from 2396 * @param {Object} o The metadata to include 2397 * @param {String} type JS or CSS 2398 * @param {Boolean} [skipcalc=false] Do a Loader.calculate on the meta 2399 */ 2400 _insert: function(source, o, type, skipcalc) { 2401 2402 2403 // restore the state at the time of the request 2404 if (source) { 2405 this._config(source); 2406 } 2407 2408 // build the dependency list 2409 // don't include type so we can process CSS and script in 2410 // one pass when the type is not specified. 2411 2412 var modules = this.resolve(!skipcalc), 2413 self = this, comp = 0, actions = 0, 2414 mods = {}, deps, complete; 2415 2416 self._refetch = []; 2417 2418 if (type) { 2419 //Filter out the opposite type and reset the array so the checks later work 2420 modules[((type === JS) ? CSS : JS)] = []; 2421 } 2422 if (!self.fetchCSS) { 2423 modules.css = []; 2424 } 2425 if (modules.js.length) { 2426 comp++; 2427 } 2428 if (modules.css.length) { 2429 comp++; 2430 } 2431 2432 //console.log('Resolved Modules: ', modules); 2433 2434 complete = function(d) { 2435 actions++; 2436 var errs = {}, i = 0, o = 0, u = '', fn, 2437 modName, resMods; 2438 2439 if (d && d.errors) { 2440 for (i = 0; i < d.errors.length; i++) { 2441 if (d.errors[i].request) { 2442 u = d.errors[i].request.url; 2443 } else { 2444 u = d.errors[i]; 2445 } 2446 errs[u] = u; 2447 } 2448 } 2449 2450 if (d && d.data && d.data.length && (d.type === 'success')) { 2451 for (i = 0; i < d.data.length; i++) { 2452 self.inserted[d.data[i].name] = true; 2453 //If the external module has a skin or a lang, reprocess it 2454 if (d.data[i].lang || d.data[i].skinnable) { 2455 delete self.inserted[d.data[i].name]; 2456 self._refetch.push(d.data[i].name); 2457 } 2458 } 2459 } 2460 2461 if (actions === comp) { 2462 self._loading = null; 2463 if (self._refetch.length) { 2464 //Get the deps for the new meta-data and reprocess 2465 for (i = 0; i < self._refetch.length; i++) { 2466 deps = self.getRequires(self.getModule(self._refetch[i])); 2467 for (o = 0; o < deps.length; o++) { 2468 if (!self.inserted[deps[o]]) { 2469 //We wouldn't be to this point without the module being here 2470 mods[deps[o]] = deps[o]; 2471 } 2472 } 2473 } 2474 mods = Y.Object.keys(mods); 2475 if (mods.length) { 2476 self.require(mods); 2477 resMods = self.resolve(true); 2478 if (resMods.cssMods.length) { 2479 for (i=0; i < resMods.cssMods.length; i++) { 2480 modName = resMods.cssMods[i].name; 2481 delete YUI.Env._cssLoaded[modName]; 2482 if (self.isCSSLoaded(modName)) { 2483 self.inserted[modName] = true; 2484 delete self.required[modName]; 2485 } 2486 } 2487 self.sorted = []; 2488 self._sort(); 2489 } 2490 d = null; //bail 2491 self._insert(); //insert the new deps 2492 } 2493 } 2494 if (d && d.fn) { 2495 fn = d.fn; 2496 delete d.fn; 2497 fn.call(self, d); 2498 } 2499 } 2500 }; 2501 2502 this._loading = true; 2503 2504 if (!modules.js.length && !modules.css.length) { 2505 actions = -1; 2506 complete({ 2507 fn: self._onSuccess 2508 }); 2509 return; 2510 } 2511 2512 2513 if (modules.css.length) { //Load CSS first 2514 Y.Get.css(modules.css, { 2515 data: modules.cssMods, 2516 attributes: self.cssAttributes, 2517 insertBefore: self.insertBefore, 2518 charset: self.charset, 2519 timeout: self.timeout, 2520 context: self, 2521 onProgress: function(e) { 2522 self._onProgress.call(self, e); 2523 }, 2524 onTimeout: function(d) { 2525 self._onTimeout.call(self, d); 2526 }, 2527 onSuccess: function(d) { 2528 d.type = 'success'; 2529 d.fn = self._onSuccess; 2530 complete.call(self, d); 2531 }, 2532 onFailure: function(d) { 2533 d.type = 'failure'; 2534 d.fn = self._onFailure; 2535 complete.call(self, d); 2536 } 2537 }); 2538 } 2539 2540 if (modules.js.length) { 2541 Y.Get.js(modules.js, { 2542 data: modules.jsMods, 2543 insertBefore: self.insertBefore, 2544 attributes: self.jsAttributes, 2545 charset: self.charset, 2546 timeout: self.timeout, 2547 autopurge: false, 2548 context: self, 2549 async: self.async, 2550 onProgress: function(e) { 2551 self._onProgress.call(self, e); 2552 }, 2553 onTimeout: function(d) { 2554 self._onTimeout.call(self, d); 2555 }, 2556 onSuccess: function(d) { 2557 d.type = 'success'; 2558 d.fn = self._onSuccess; 2559 complete.call(self, d); 2560 }, 2561 onFailure: function(d) { 2562 d.type = 'failure'; 2563 d.fn = self._onFailure; 2564 complete.call(self, d); 2565 } 2566 }); 2567 } 2568 }, 2569 /** 2570 * Once a loader operation is completely finished, process any additional queued items. 2571 * @method _continue 2572 * @private 2573 */ 2574 _continue: function() { 2575 if (!(_queue.running) && _queue.size() > 0) { 2576 _queue.running = true; 2577 _queue.next()(); 2578 } 2579 }, 2580 2581 /** 2582 * inserts the requested modules and their dependencies. 2583 * <code>type</code> can be "js" or "css". Both script and 2584 * css are inserted if type is not provided. 2585 * @method insert 2586 * @param {object} o optional options object. 2587 * @param {string} type the type of dependency to insert. 2588 */ 2589 insert: function(o, type, skipsort) { 2590 var self = this, copy = Y.merge(this); 2591 delete copy.require; 2592 delete copy.dirty; 2593 _queue.add(function() { 2594 self._insert(copy, o, type, skipsort); 2595 }); 2596 this._continue(); 2597 }, 2598 2599 /** 2600 * Executed every time a module is loaded, and if we are in a load 2601 * cycle, we attempt to load the next script. Public so that it 2602 * is possible to call this if using a method other than 2603 * Y.register to determine when scripts are fully loaded 2604 * @method loadNext 2605 * @deprecated 2606 * @param {string} mname optional the name of the module that has 2607 * been loaded (which is usually why it is time to load the next 2608 * one). 2609 */ 2610 loadNext: function() { 2611 return; 2612 }, 2613 2614 /** 2615 * Apply filter defined for this instance to a url/path 2616 * @method _filter 2617 * @param {string} u the string to filter. 2618 * @param {string} name the name of the module, if we are processing 2619 * a single module as opposed to a combined url. 2620 * @return {string} the filtered string. 2621 * @private 2622 */ 2623 _filter: function(u, name, group) { 2624 var f = this.filter, 2625 hasFilter = name && (name in this.filters), 2626 modFilter = hasFilter && this.filters[name], 2627 groupName = group || (this.getModuleInfo(name) || {}).group || null; 2628 2629 if (groupName && this.groups[groupName] && this.groups[groupName].filter) { 2630 modFilter = this.groups[groupName].filter; 2631 hasFilter = true; 2632 } 2633 2634 if (u) { 2635 if (hasFilter) { 2636 f = (L.isString(modFilter)) ? this.FILTER_DEFS[modFilter.toUpperCase()] || null : modFilter; 2637 } 2638 if (f) { 2639 u = u.replace(new RegExp(f.searchExp, 'g'), f.replaceStr); 2640 } 2641 } 2642 return u; 2643 }, 2644 2645 /** 2646 * Generates the full url for a module 2647 * @method _url 2648 * @param {string} path the path fragment. 2649 * @param {String} name The name of the module 2650 * @param {String} [base] The base url to use. Defaults to self.base 2651 * @return {string} the full url. 2652 * @private 2653 */ 2654 _url: function(path, name, base) { 2655 return this._filter((base || this.base || '') + path, name); 2656 }, 2657 /** 2658 * Returns an Object hash of file arrays built from `loader.sorted` or from an arbitrary list of sorted modules. 2659 * @method resolve 2660 * @param {Boolean} [calc=false] Perform a loader.calculate() before anything else 2661 * @param {Array} [sorted=loader.sorted] An override for the loader.sorted array 2662 * @return {Object} Object hash (js and css) of two arrays of file lists 2663 * @example This method can be used as an off-line dep calculator 2664 * 2665 * var Y = YUI(); 2666 * var loader = new Y.Loader({ 2667 * filter: 'debug', 2668 * base: '../../', 2669 * root: 'build/', 2670 * combine: true, 2671 * require: ['node', 'dd', 'console'] 2672 * }); 2673 * var out = loader.resolve(true); 2674 * 2675 */ 2676 resolve: function(calc, sorted) { 2677 var self = this, 2678 resolved = { js: [], jsMods: [], css: [], cssMods: [] }, 2679 addSingle; 2680 2681 if (self.skin.overrides || self.skin.defaultSkin !== DEFAULT_SKIN || self.ignoreRegistered) { 2682 self._resetModules(); 2683 } 2684 2685 if (calc) { 2686 self.calculate(); 2687 } 2688 sorted = sorted || self.sorted; 2689 2690 addSingle = function(mod) { 2691 if (mod) { 2692 var group = (mod.group && self.groups[mod.group]) || NOT_FOUND, 2693 url; 2694 2695 //Always assume it's async 2696 if (group.async === false) { 2697 mod.async = group.async; 2698 } 2699 2700 url = (mod.fullpath) ? self._filter(mod.fullpath, mod.name) : 2701 self._url(mod.path, mod.name, group.base || mod.base); 2702 2703 if (mod.attributes || mod.async === false) { 2704 url = { 2705 url: url, 2706 async: mod.async 2707 }; 2708 if (mod.attributes) { 2709 url.attributes = mod.attributes; 2710 } 2711 } 2712 resolved[mod.type].push(url); 2713 resolved[mod.type + 'Mods'].push(mod); 2714 } else { 2715 } 2716 2717 }; 2718 2719 /*jslint vars: true */ 2720 var inserted = (self.ignoreRegistered) ? {} : self.inserted, 2721 comboSources = {}, 2722 maxURLLength, 2723 comboMeta, 2724 comboBase, 2725 comboSep, 2726 group, 2727 mod, 2728 len, 2729 i; 2730 /*jslint vars: false */ 2731 2732 for (i = 0, len = sorted.length; i < len; i++) { 2733 mod = self.getModule(sorted[i]); 2734 if (!mod || inserted[mod.name]) { 2735 continue; 2736 } 2737 2738 group = self.groups[mod.group]; 2739 2740 comboBase = self.comboBase; 2741 2742 if (group) { 2743 if (!group.combine || mod.fullpath) { 2744 //This is not a combo module, skip it and load it singly later. 2745 addSingle(mod); 2746 continue; 2747 } 2748 mod.combine = true; 2749 2750 if (typeof group.root === 'string') { 2751 mod.root = group.root; 2752 } 2753 2754 comboBase = group.comboBase || comboBase; 2755 comboSep = group.comboSep; 2756 maxURLLength = group.maxURLLength; 2757 } else { 2758 if (!self.combine) { 2759 //This is not a combo module, skip it and load it singly later. 2760 addSingle(mod); 2761 continue; 2762 } 2763 } 2764 2765 if (!mod.combine && mod.ext) { 2766 addSingle(mod); 2767 continue; 2768 } 2769 2770 comboSources[comboBase] = comboSources[comboBase] || 2771 { js: [], jsMods: [], css: [], cssMods: [] }; 2772 2773 comboMeta = comboSources[comboBase]; 2774 comboMeta.group = mod.group; 2775 comboMeta.comboSep = comboSep || self.comboSep; 2776 comboMeta.maxURLLength = maxURLLength || self.maxURLLength; 2777 2778 comboMeta[mod.type + 'Mods'].push(mod); 2779 } 2780 2781 // TODO: Refactor the encoding logic below into its own method. 2782 2783 /*jslint vars: true */ 2784 var fragSubset, 2785 modules, 2786 tmpBase, 2787 baseLen, 2788 frags, 2789 frag, 2790 type; 2791 /*jslint vars: false */ 2792 2793 for (comboBase in comboSources) { 2794 if (comboSources.hasOwnProperty(comboBase)) { 2795 comboMeta = comboSources[comboBase]; 2796 comboSep = comboMeta.comboSep; 2797 maxURLLength = comboMeta.maxURLLength; 2798 for (type in comboMeta) { 2799 if (type === JS || type === CSS) { 2800 modules = comboMeta[type + 'Mods']; 2801 frags = []; 2802 for (i = 0, len = modules.length; i < len; i += 1) { 2803 mod = modules[i]; 2804 frag = ((typeof mod.root === 'string') ? mod.root : self.root) + (mod.path || mod.fullpath); 2805 frags.push( 2806 self._filter(frag, mod.name) 2807 ); 2808 } 2809 tmpBase = comboBase + frags.join(comboSep); 2810 baseLen = tmpBase.length; 2811 if (maxURLLength <= comboBase.length) { 2812 maxURLLength = MAX_URL_LENGTH; 2813 } 2814 2815 if (frags.length) { 2816 if (baseLen > maxURLLength) { 2817 fragSubset = []; 2818 for (i = 0, len = frags.length; i < len; i++) { 2819 fragSubset.push(frags[i]); 2820 tmpBase = comboBase + fragSubset.join(comboSep); 2821 2822 if (tmpBase.length > maxURLLength) { 2823 frag = fragSubset.pop(); 2824 tmpBase = comboBase + fragSubset.join(comboSep); 2825 resolved[type].push(self._filter(tmpBase, null, comboMeta.group)); 2826 fragSubset = []; 2827 if (frag) { 2828 fragSubset.push(frag); 2829 } 2830 } 2831 } 2832 if (fragSubset.length) { 2833 tmpBase = comboBase + fragSubset.join(comboSep); 2834 resolved[type].push(self._filter(tmpBase, null, comboMeta.group)); 2835 } 2836 } else { 2837 resolved[type].push(self._filter(tmpBase, null, comboMeta.group)); 2838 } 2839 } 2840 resolved[type + 'Mods'] = resolved[type + 'Mods'].concat(modules); 2841 } 2842 } 2843 } 2844 } 2845 2846 return resolved; 2847 }, 2848 2849 /** 2850 Shortcut to calculate, resolve and load all modules. 2851 2852 var loader = new Y.Loader({ 2853 ignoreRegistered: true, 2854 modules: { 2855 mod: { 2856 path: 'mod.js' 2857 } 2858 }, 2859 requires: [ 'mod' ] 2860 }); 2861 loader.load(function() { 2862 console.log('All modules have loaded..'); 2863 }); 2864 2865 2866 @method load 2867 @param {Function} cb Executed after all load operations are complete 2868 */ 2869 load: function(cb) { 2870 if (!cb) { 2871 return; 2872 } 2873 var self = this, 2874 out = self.resolve(true); 2875 2876 self.data = out; 2877 2878 self.onEnd = function() { 2879 cb.apply(self.context || self, arguments); 2880 }; 2881 2882 self.insert(); 2883 } 2884 }; 2885 2886 2887 2888 }, '3.17.2', {"requires": ["get", "features"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |