API Docs for: 3.17.2
Show:

File: loader/js/loader.js

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