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