2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 YUI.add('loader', function(Y) {
12 * Loader dynamically loads script and css files. It includes the dependency
13 * info for the version of the library in use, and will automatically pull in
14 * dependencies for the modules requested. It supports rollup files and will
15 * automatically use these when appropriate in order to minimize the number of
16 * http connections required to load all of the dependencies. It can load the
17 * files from the Yahoo! CDN, and it can utilize the combo service provided on
18 * this network to reduce the number of http connections required to download
25 * Loader dynamically loads script and css files. It includes the dependency
26 * info for the version of the library in use, and will automatically pull in
27 * dependencies for the modules requested. It supports rollup files and will
28 * automatically use these when appropriate in order to minimize the number of
29 * http connections required to load all of the dependencies. It can load the
30 * files from the Yahoo! CDN, and it can utilize the combo service provided on
31 * this network to reduce the number of http connections required to download
34 * While the loader can be instantiated by the end user, it normally is not.
35 * @see YUI.use for the normal use case. The use function automatically will
36 * pull in missing dependencies.
40 * @param o an optional set of configuration options. Valid options:
45 * The secure base dir (not implemented)</li>
47 * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?</li>
49 * The root path to prepend to module names for the combo service. Ex: 2.5.2/build/</li>
52 * A filter to apply to result urls. This filter will modify the default
53 * path for all modules. The default path for the YUI library is the
54 * minified version of the files (e.g., event-min.js). The filter property
55 * can be a predefined filter or a custom filter. The valid predefined
59 * <dd>Selects the debug versions of the library (e.g., event-debug.js).
60 * This option will automatically include the Logger widget</dd>
62 * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
64 * You can also define a custom filter, which must be an object literal
65 * containing a search expression and a replace string:
68 * 'searchExp': "-min\\.js",
69 * 'replaceStr': "-debug.js"
74 * <li>filters: per-component filter specification. If specified for a given component, this overrides the filter config</li>
76 * Use the YUI combo service to reduce the number of http connections required to load your dependencies</li>
78 * A list of modules that should never be dynamically loaded</li>
80 * A list of modules that should always be loaded when required, even if already present on the page</li>
82 * Node or id for a node that should be used as the insertion point for new nodes</li>
84 * charset for dynamic nodes (deprecated, use jsAttributes or cssAttributes)</li>
85 * <li>jsAttributes: object literal containing attributes to add to script nodes</li>
86 * <li>cssAttributes: object literal containing attributes to add to link nodes</li>
88 * number of milliseconds before a timeout occurs when dynamically loading nodes. in not set, there is no timeout</li>
90 * execution context for all callbacks</li>
92 * callback for the 'success' event</li>
93 * <li>onFailure: callback for the 'failure' event</li>
94 * <li>onCSS: callback for the 'CSSComplete' event. When loading YUI components with CSS
95 * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
96 * the presentation of the page while the script is loading.</li>
98 * callback for the 'timeout' event</li>
100 * callback executed each time a script or css file is loaded</li>
102 * A list of module definitions. See Loader.addModule for the supported module metadata</li>
107 * Global loader queue
108 * @property _loaderQueue
112 YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Y.Queue();
115 GLOBAL_ENV = YUI.Env,
120 CSSRESET = 'cssreset',
121 CSSFONTS = 'cssfonts',
122 CSSGRIDS = 'cssgrids',
124 CSS_AFTER = [CSSRESET, CSSFONTS, CSSGRIDS,
125 'cssreset-context', 'cssfonts-context', 'cssgrids-context'],
126 YUI_CSS = ['reset', 'fonts', 'grids', BASE],
128 ROOT = VERSION + '/build/',
129 CONTEXT = '-context',
131 ANIMBASE = 'anim-base',
132 ATTRIBUTE = 'attribute',
133 ATTRIBUTEBASE = ATTRIBUTE + '-base',
134 BASEBASE = 'base-base',
137 DATASCHEMABASE = 'dataschema-base',
138 DATASOURCELOCAL = 'datasource-local',
139 DOMBASE = 'dom-base',
140 DOMSTYLE = 'dom-style',
141 DOMSCREEN = 'dom-screen',
144 EVENTBASE = 'event-base',
145 EVENTCUSTOM = 'event-custom',
146 EVENTCUSTOMBASE = 'event-custom-base',
149 NODEBASE = 'node-base',
150 NODESTYLE = 'node-style',
151 NODESCREEN = 'node-screen',
153 PLUGINHOST = 'pluginhost',
154 SELECTORCSS2 = 'selector-css2',
155 SUBSTITUTE = 'substitute',
157 WIDGETPOSITION = 'widget-position',
158 YUIBASE = 'yui-base',
168 base: 'http://yui.yahooapis.com/' + ROOT,
170 comboBase: 'http://yui.yahooapis.com/combo?',
174 base: 'assets/skins/',
195 requires: [DOMBASE, DOMSTYLE]
203 requires: ['selector-native']
214 requires: [SELECTORCSS2]
220 requires: [DOM, EVENTBASE],
225 requires: [DOMBASE, SELECTORCSS2, EVENTBASE]
229 requires: [DOMSTYLE, NODEBASE]
233 requires: [DOMSCREEN, NODEBASE]
237 requires: [NODEBASE, PLUGINHOST]
241 'node-event-delegate': {
242 requires: [NODEBASE, 'event-delegate']
247 'node-event-simulate': {
248 requires: [NODEBASE, 'event-simulate']
257 requires: [BASEBASE, NODESTYLE]
273 requires: [ANIMBASE, NODESCREEN]
277 requires: ['anim-xy']
280 'anim-node-plugin': {
281 requires: ['node-pluginhost', ANIMBASE]
289 requires: [EVENTCUSTOM]
292 'attribute-complex': {
293 requires: [ATTRIBUTEBASE]
301 requires: [ATTRIBUTEBASE]
309 requires: [BASEBASE, PLUGINHOST]
319 requires: [NODE, DUMP, SUBSTITUTE]
331 requires: ['yui-log', WIDGET, SUBSTITUTE],
335 requires: [PLUGIN, 'console'],
350 'dataschema-array': {
351 requires: [DATASCHEMABASE]
354 requires: [DATASCHEMABASE, 'json']
357 requires: [DATASCHEMABASE]
360 requires: [DATASCHEMABASE]
367 'datasource-local': {
370 'datasource-arrayschema': {
371 requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-array']
373 'datasource-cache': {
374 requires: [DATASOURCELOCAL, 'cache']
376 'datasource-function': {
377 requires: [DATASOURCELOCAL]
379 'datasource-jsonschema': {
380 requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-json']
382 'datasource-polling': {
383 requires: [DATASOURCELOCAL]
386 requires: [DATASOURCELOCAL, GET]
388 'datasource-textschema': {
389 requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-text']
392 requires: [DATASOURCELOCAL, IOBASE]
394 'datasource-xmlschema': {
395 requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-xml']
417 requires: [NODE, BASE]
420 requires: ['dd-ddm-base', 'event-resize']
426 requires: ['dd-ddm-base']
429 requires: ['dd-ddm-drop']
442 optional: ['dd-constrain', 'dd-proxy']
445 requires: ['dd-drop']
459 requires: [EVENTCUSTOMBASE]
470 'event-mouseenter': {
473 'event-mousewheel': {
484 'event-custom-base': {
485 requires: [OOP, 'yui-later']
487 'event-custom-complex': {
488 requires: [EVENTCUSTOMBASE]
494 requires: [EVENTBASE]
497 'node-focusmanager': {
498 requires: [ATTRIBUTE, NODE, PLUGIN, 'node-event-simulate', 'event-key', 'event-focus']
506 requires: [BASEBASE, NODESTYLE, NODESCREEN]
513 requires: [EVENTCUSTOMBASE]
517 requires: [IOBASE, 'datatype-xml']
521 requires: [IOBASE, NODEBASE, NODESTYLE]
524 'io-upload-iframe': {
525 requires: [IOBASE, NODEBASE]
529 requires: [IOBASE, 'queue-promote']
551 requires: [NODE, 'classnamemanager', PLUGIN, 'node-focusmanager'],
560 requires: [WIDGET, WIDGETPOSITION, 'widget-position-ext', 'widget-stack', 'widget-stdmod'],
580 // deprecated package, replaced with async-queue
582 requires: [EVENTCUSTOM],
583 path: 'async-queue/async-queue-min.js'
587 requires: [EVENTCUSTOM],
588 supersedes: ['queue-run']
592 requires: [WIDGET, 'dd-constrain'],
605 requires: [ATTRIBUTE, 'event-focus', BASE, NODE, 'classnamemanager'],
607 'widget-position': { },
608 'widget-position-ext': {
609 requires: [WIDGETPOSITION]
629 requires: [SUBSTITUTE, NODE, 'json', 'event-simulate']
635 _path = Y.cached(function(dir, file, type) {
636 return dir + '/' + file + '-min.' + (type || CSS);
639 _queue = YUI.Env._loaderQueue,
641 mods = META.modules, i, bname, mname, contextname,
644 // Create the metadata for both the regular and context-aware
645 // versions of the YUI CSS foundation.
646 for (i=0; i<YUI_CSS.length; i=i+1) {
652 path: _path(mname, bname)
655 // define -context module
656 contextname = mname + CONTEXT;
657 bname = bname + CONTEXT;
659 mods[contextname] = {
661 path: _path(mname, bname)
664 if (mname == CSSGRIDS) {
665 mods[mname].requires = [CSSFONTS];
666 mods[mname].optional = [CSSRESET];
667 mods[contextname].requires = [CSSFONTS + CONTEXT];
668 mods[contextname].optional = [CSSRESET + CONTEXT];
669 } else if (mname == CSSBASE) {
670 mods[mname].after = CSS_AFTER;
671 mods[contextname].after = CSS_AFTER;
677 GLOBAL_LOADED = GLOBAL_ENV._loaded;
679 Y.Loader = function(o) {
682 * Internal callback to handle multiple internal insert() calls
683 * so that css is inserted prior to js
684 * @property _internalCallback
687 // this._internalCallback = null;
690 * Callback that will be executed when the loader is finished
695 // this.onSuccess = null;
698 * Callback that will be executed if there is a failure
702 // this.onFailure = null;
705 * Callback for the 'CSSComplete' event. When loading YUI components with CSS
706 * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
707 * the presentation of the page while the script is loading.
711 // this.onCSS = null;
714 * Callback executed each time a script or css file is loaded
718 // this.onProgress = null;
721 * Callback that will be executed if a timeout occurs
725 // this.onTimeout = null;
728 * The execution context for all callbacks
730 * @default {YUI} the YUI instance
735 * Data that is passed to all callbacks
741 * Node reference or id where new nodes should be inserted before
742 * @property insertBefore
743 * @type string|HTMLElement
745 // this.insertBefore = null;
748 * The charset attribute for inserted nodes
751 * @deprecated, use cssAttributes or jsAttributes
753 // this.charset = null;
756 * An object literal containing attributes to add to link nodes
757 * @property cssAttributes
760 // this.cssAttributes = null;
763 * An object literal containing attributes to add to script nodes
764 * @property jsAttributes
767 // this.jsAttributes = null;
770 * The base directory.
773 * @default http://yui.yahooapis.com/[YUI VERSION]/build/
775 this.base = Y.Env.meta.base;
778 * Base path for the combo service
779 * @property comboBase
781 * @default http://yui.yahooapis.com/combo?
783 this.comboBase = Y.Env.meta.comboBase;
786 * If configured, YUI JS resources will use the combo
790 * @default true if a base dir isn't in the config
792 this.combine = o.base && (o.base.indexOf( this.comboBase.substr(0, 20)) > -1);
795 * Ignore modules registered on the YUI global
796 * @property ignoreRegistered
799 // this.ignoreRegistered = false;
802 * Root path to prepend to module path for the combo
806 * @default [YUI VERSION]/build/
808 this.root = Y.Env.meta.root;
811 * Timeout value in milliseconds. If set, this value will be used by
812 * the get utility. the timeout event will fire if
820 * A list of modules that should not be loaded, even if
821 * they turn up in the dependency tree
825 // this.ignore = null;
828 * A list of modules that should always be loaded, even
829 * if they have already been inserted into the page.
833 // this.force = null;
838 * Should we allow rollups
839 * @property allowRollup
843 // this.allowRollup = true;
846 * A filter to apply to result urls. This filter will modify the default
847 * path for all modules. The default path for the YUI library is the
848 * minified version of the files (e.g., event-min.js). The filter property
849 * can be a predefined filter or a custom filter. The valid predefined
853 * <dd>Selects the debug versions of the library (e.g., event-debug.js).
854 * This option will automatically include the Logger widget</dd>
856 * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
858 * You can also define a custom filter, which must be an object literal
859 * containing a search expression and a replace string:
862 * 'searchExp': "-min\\.js",
863 * 'replaceStr': "-debug.js"
867 * @type string|{searchExp: string, replaceStr: string}
869 // this.filter = null;
872 * per-component filter specification. If specified for a given component, this
873 * overrides the filter config.
880 * The list of requested modules
882 * @type {string: boolean}
887 * The library metadata
888 * @property moduleInfo
890 // this.moduleInfo = Y.merge(Y.Env.meta.moduleInfo);
891 this.moduleInfo = {};
894 * Provides the information used to skin the skinnable components.
895 * The following skin definition would result in 'skin1' and 'skin2'
896 * being loaded for calendar (if calendar was requested), and
897 * 'sam' for all other skinnable components:
902 * // The default skin, which is automatically applied if not
903 * // overriden by a component-specific skin definition.
904 * // Change this in to apply a different skin globally
905 * defaultSkin: 'sam',
907 * // This is combined with the loader base property to get
908 * // the default root directory for a skin. ex:
909 * // http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/
910 * base: 'assets/skins/',
912 * // The name of the rollup css file for the skin
915 * // The number of skinnable components requested that are
916 * // required before using the rollup file rather than the
917 * // individual component css files
920 * // Any component-specific overrides can be specified here,
921 * // making it possible to load different skins for different
922 * // components. It is possible to load more than one skin
923 * // for a given component as well.
925 * calendar: ['skin1', 'skin2']
931 this.skin = Y.merge(Y.Env.meta.skin);
933 var defaults = Y.Env.meta.modules, i, onPage = YUI.Env.mods;
935 this._internal = true;
936 for (i in defaults) {
937 if (defaults.hasOwnProperty(i)) {
938 this.addModule(defaults[i], i);
943 if (onPage.hasOwnProperty(i) && !this.moduleInfo[i] && onPage[i].details) {
944 this.addModule(onPage[i].details, i);
947 this._internal = false;
950 * List of rollup files found in the library metadata
953 // this.rollups = null;
956 * Whether or not to load optional dependencies for
957 * the requested modules
958 * @property loadOptional
962 // this.loadOptional = false;
965 * All of the derived dependencies in sorted order, which
966 * will be populated when either calculate() or insert()
974 * Set when beginning to compute the dependency tree.
975 * Composed of what YUI reports to be loaded combined
976 * with what has been loaded by any instance on the page
977 * with the version number specified in the metadata.
979 * @type {string: boolean}
981 this.loaded = GLOBAL_LOADED[VERSION];
984 * A list of modules to attach to the YUI instance when complete.
985 * If not supplied, the sorted list of dependencies are applied.
986 * @property attaching
988 // this.attaching = null;
991 * Flag to indicate the dependency tree needs to be recomputed
992 * if insert is called again.
1000 * List of modules inserted by the utility
1001 * @property inserted
1002 * @type {string: boolean}
1007 * List of skipped modules during insert() because the module
1014 // Y.on('yui:load', this.loadNext, this);
1020 Y.Loader.prototype = {
1024 'searchExp': "-min\\.js",
1028 'searchExp': "-min\\.js",
1029 'replaceStr': "-debug.js"
1033 SKIN_PREFIX: "skin-",
1035 _config: function(o) {
1039 // apply config values
1042 if (o.hasOwnProperty(i)) {
1044 if (i == 'require') {
1046 } else if (i == 'modules') {
1048 // add a hash of module definitions
1050 if (val.hasOwnProperty(j)) {
1051 this.addModule(val[j], j);
1065 if (L.isString(f)) {
1066 f = f.toUpperCase();
1067 this.filterName = f;
1068 this.filter = this.FILTER_DEFS[f];
1070 this.require('yui-log', 'dump');
1077 * Returns the skin module name for the specified skin name. If a
1078 * module name is supplied, the returned skin module name is
1079 * specific to the module passed in.
1080 * @method formatSkin
1081 * @param skin {string} the name of the skin
1082 * @param mod {string} optional: the name of a module to skin
1083 * @return {string} the full skin module name
1085 formatSkin: function(skin, mod) {
1086 var s = this.SKIN_PREFIX + skin;
1095 * Reverses <code>formatSkin</code>, providing the skin name and
1096 * module name if the string matches the pattern for skins.
1098 * @param mod {string} the module name to parse
1099 * @return {skin: string, module: string} the parsed skin name
1100 * and module name, or null if the supplied string does not match
1103 * This isn't being used at the moment
1106 // parseSkin: function(mod) {
1108 // if (mod.indexOf(this.SKIN_PREFIX) === 0) {
1109 // var a = mod.split("-");
1110 // return {skin: a[1], module: a[2]};
1116 * Adds the skin def to the module info
1118 * @param skin {string} the name of the skin
1119 * @param mod {string} the name of the module
1120 * @param parent {string} parent module if this is a skin of a
1121 * submodule or plugin
1122 * @return {string} the module name for the skin
1125 _addSkin: function(skin, mod, parent) {
1127 var name = this.formatSkin(skin),
1128 info = this.moduleInfo,
1130 ext = info[mod] && info[mod].ext,
1134 // Add a module definition for the skin rollup css
1139 'path': sinf.base + skin + '/' + sinf.path,
1140 //'supersedes': '*',
1141 'after': sinf.after,
1142 'rollup': sinf.rollup,
1148 // Add a module definition for the module-specific skin css
1150 name = this.formatSkin(skin, mod);
1153 pkg = mdef.pkg || mod;
1157 'after': sinf.after,
1158 'path': (parent || pkg) + '/' + sinf.base + skin + '/' + mod + '.css',
1167 /** Add a new module to the component metadata.
1169 * <dt>name:</dt> <dd>required, the component name</dd>
1170 * <dt>type:</dt> <dd>required, the component type (js or css)</dd>
1171 * <dt>path:</dt> <dd>required, the path to the script from "base"</dd>
1172 * <dt>requires:</dt> <dd>array of modules required by this component</dd>
1173 * <dt>optional:</dt> <dd>array of optional modules for this component</dd>
1174 * <dt>supersedes:</dt> <dd>array of the modules this component replaces</dd>
1175 * <dt>after:</dt> <dd>array of modules the components which, if present, should be sorted above this one</dd>
1176 * <dt>rollup:</dt> <dd>the number of superseded modules required for automatic rollup</dd>
1177 * <dt>fullpath:</dt> <dd>If fullpath is specified, this is used instead of the configured base + path</dd>
1178 * <dt>skinnable:</dt> <dd>flag to determine if skin assets should automatically be pulled in</dd>
1179 * <dt>submodules:</dt> <dd>a has of submodules</dd>
1182 * @param o An object containing the module data
1183 * @param name the module name (optional), required if not in the module data
1184 * @return {boolean} true if the module was added, false if
1185 * the object passed in did not provide all required attributes
1187 addModule: function(o, name) {
1189 name = name || o.name;
1192 if (!o || !o.name) {
1200 if (!o.path && !o.fullpath) {
1201 // o.path = name + "/" + name + "-min." + o.type;
1202 o.path = _path(name, name, o.type);
1205 o.ext = ('ext' in o) ? o.ext : (this._internal) ? false : true;
1206 o.requires = o.requires || [];
1209 this.moduleInfo[name] = o;
1211 // Handle submodule logic
1212 var subs = o.submodules, i, l, sup, s, smod, plugins, plug;
1218 if (subs.hasOwnProperty(i)) {
1220 s.path = _path(name, i, o.type);
1221 this.addModule(s, i);
1225 smod = this._addSkin(this.skin.defaultSkin, i, name);
1226 sup.push(smod.name);
1234 o.rollup = (l<4) ? l : Math.min(l-1, 4);
1237 plugins = o.plugins;
1239 for (i in plugins) {
1240 if (plugins.hasOwnProperty(i)) {
1242 plug.path = _path(name, i, o.type);
1243 plug.requires = plug.requires || [];
1244 // plug.requires.push(name);
1245 this.addModule(plug, i);
1247 this._addSkin(this.skin.defaultSkin, i, name);
1259 * Add a requirement for one or more module
1261 * @param what {string[] | string*} the modules to load
1263 require: function(what) {
1264 var a = (typeof what === "string") ? arguments : what;
1266 Y.mix(this.required, Y.Array.hash(a));
1270 * Returns an object containing properties for all modules required
1271 * in order to load the requested module
1272 * @method getRequires
1273 * @param mod The module definition from moduleInfo
1275 getRequires: function(mod) {
1281 if (!this.dirty && mod.expanded) {
1282 return mod.expanded;
1285 var i, d=[], r=mod.requires, o=mod.optional,
1286 info=this.moduleInfo, m, j, add;
1288 for (i=0; i<r.length; i=i+1) {
1290 m = this.getModule(r[i]);
1291 add = this.getRequires(m);
1292 for (j=0;j<add.length;j=j+1) {
1297 // get the requirements from superseded modules, if any
1300 for (i=0; i<r.length; i=i+1) {
1302 m = this.getModule(r[i]);
1303 add = this.getRequires(m);
1304 for (j=0;j<add.length;j=j+1) {
1310 if (o && this.loadOptional) {
1311 for (i=0; i<o.length; i=i+1) {
1313 add = this.getRequires(info[o[i]]);
1314 for (j=0;j<add.length;j=j+1) {
1320 mod.expanded = Y.Object.keys(Y.Array.hash(d));
1321 return mod.expanded;
1326 * Returns a hash of module names the supplied module satisfies.
1327 * @method getProvides
1328 * @param name {string} The name of the module
1329 * @return what this module provides
1331 getProvides: function(name) {
1332 var m = this.getModule(name), o, s;
1338 if (m && !m.provides) {
1343 Y.Array.each(s, function(v) {
1344 Y.mix(o, this.getProvides(v));
1357 * Calculates the dependency tree, the result is stored in the sorted
1360 * @param o optional options object
1361 * @param type optional argument to prune modules
1363 calculate: function(o, type) {
1364 if (o || type || this.dirty) {
1368 if (this.allowRollup && !this.combine) {
1380 * Investigates the current YUI configuration on the page. By default,
1381 * modules already detected will not be loaded again unless a force
1382 * option is encountered. Called by calculate()
1386 _setup: function() {
1388 var info = this.moduleInfo, name, i, j, m, o, l, smod;
1390 // Create skin modules
1391 for (name in info) {
1392 if (info.hasOwnProperty(name)) {
1394 if (m && m.skinnable) {
1395 o = this.skin.overrides;
1397 for (i=0; i<o[name].length; i=i+1) {
1398 smod = this._addSkin(o[name][i], name);
1401 smod = this._addSkin(this.skin.defaultSkin, name);
1404 m.requires.push(smod);
1409 l = Y.merge(this.inserted); // shallow clone
1411 // available modules
1412 if (!this.ignoreRegistered) {
1413 Y.mix(l, GLOBAL_ENV.mods);
1417 // add the ignore list to the list of loaded packages
1419 // OU.appendArray(l, this.ignore);
1420 Y.mix(l, Y.Array.hash(this.ignore));
1423 // expand the list to include superseded modules
1425 if (l.hasOwnProperty(j)) {
1426 Y.mix(l, this.getProvides(j));
1430 // remove modules on the force list from the loaded list
1432 for (i=0; i<this.force.length; i=i+1) {
1433 if (this.force[i] in l) {
1434 delete l[this.force[i]];
1440 Y.mix(this.loaded, l);
1448 * Inspects the required modules list looking for additional
1449 * dependencies. Expands the required list to include all
1450 * required modules. Called by calculate()
1454 _explode: function() {
1456 var r = this.required, m, reqs;
1458 Y.Object.each(r, function(v, name) {
1460 m = this.getModule(name);
1462 var expound = m && m.expound;
1467 r[expound] = this.getModule(expound);
1468 reqs = this.getRequires(r[expound]);
1469 Y.mix(r, Y.Array.hash(reqs));
1472 reqs = this.getRequires(m);
1474 Y.mix(r, Y.Array.hash(reqs));
1480 getModule: function(name) {
1482 var m = this.moduleInfo[name];
1484 // create the default module
1486 // m = this.addModule({ext: false}, name);
1493 * Look for rollup packages to determine if all of the modules a
1494 * rollup supersedes are required. If so, include the rollup to
1495 * help reduce the total number of connections required. Called
1500 _rollup: function() {
1501 var i, j, m, s, rollups={}, r=this.required, roll,
1502 info = this.moduleInfo, rolled, c;
1504 // find and cache rollup modules
1505 if (this.dirty || !this.rollups) {
1507 if (info.hasOwnProperty(i)) {
1508 m = this.getModule(i);
1509 // if (m && m.rollup && m.supersedes) {
1510 if (m && m.rollup) {
1516 this.rollups = rollups;
1517 this.forceMap = (this.force) ? Y.Array.hash(this.force) : {};
1520 // make as many passes as needed to pick up rollup rollups
1524 // go through the rollup candidates
1525 for (i in rollups) {
1527 if (rollups.hasOwnProperty(i)) {
1529 // there can be only one, unless forced
1530 if (!r[i] && ((!this.loaded[i]) || this.forceMap[i])) {
1531 m = this.getModule(i);
1532 s = m.supersedes || [];
1535 // @TODO remove continue
1542 // check the threshold
1543 for (j=0;j<s.length;j=j+1) {
1546 // if the superseded module is loaded, we can't load the rollup
1547 // unless it has been forced
1548 if (this.loaded[s[j]] && !this.forceMap[s[j]]) {
1551 // increment the counter if this module is required. if we are
1552 // beyond the rollup threshold, we will use the rollup module
1553 } else if (r[s[j]]) {
1555 roll = (c >= m.rollup);
1567 // expand the rollup's dependencies
1568 this.getRequires(m);
1574 // if we made it here w/o rolling up something, we are done
1582 * Remove superceded modules and loaded modules. Called by
1583 * calculate() after we have the mega list of all dependencies
1587 _reduce: function() {
1588 var i, j, s, m, r=this.required, type = this.loadType;
1590 if (r.hasOwnProperty(i)) {
1591 m = this.getModule(i);
1592 // remove if already loaded
1593 if ((this.loaded[i] && (!this.forceMap[i]) && !this.ignoreRegistered) || (type && m && m.type != type)) {
1595 // remove anything this module supersedes
1598 s = m && m.supersedes;
1600 for (j=0; j<s.length; j=j+1) {
1611 _attach: function() {
1612 // this is the full list of items the YUI needs attached,
1613 // which is needed if some dependencies are already on
1614 // the page without their dependencies.
1615 if (this.attaching) {
1616 Y._attach(this.attaching);
1618 Y._attach(this.sorted);
1621 // this._pushEvents();
1625 _finish: function() {
1626 _queue.running = false;
1630 _onSuccess: function() {
1635 var skipped = this.skipped, i, f;
1637 for (i in skipped) {
1638 if (skipped.hasOwnProperty(i)) {
1639 delete this.inserted[i];
1648 f.call(this.context, {
1659 _onFailure: function(o) {
1664 var f = this.onFailure;
1666 f.call(this.context, {
1667 msg: 'failure: ' + o.msg,
1676 _onTimeout: function() {
1681 var f = this.onTimeout;
1683 f.call(this.context, {
1694 * Sorts the dependency tree. The last step of calculate()
1700 // create an indexed list
1701 var s = Y.Object.keys(this.required),
1702 info = this.moduleInfo,
1703 loaded = this.loaded,
1705 p=0, l, a, b, j, k, moved, doneKey,
1707 // returns true if b is not loaded, and is required
1708 // directly or by means of modules it supersedes.
1709 requires = Y.cached(function(mod1, mod2) {
1711 var m = info[mod1], i, r, after, other = info[mod2], s;
1713 if (loaded[mod2] || !m || !other) {
1720 // check if this module requires the other directly
1721 if (r && Y.Array.indexOf(r, mod2) > -1) {
1725 // check if this module should be sorted after the other
1726 if (after && Y.Array.indexOf(after, mod2) > -1) {
1730 // check if this module requires one the other supersedes
1731 s = info[mod2] && info[mod2].supersedes;
1733 for (i=0; i<s.length; i=i+1) {
1734 if (requires(mod1, s[i])) {
1740 // external css files should be sorted below yui css
1741 if (m.ext && m.type == CSS && !other.ext && other.type == CSS) {
1748 // keep going until we make a pass without moving anything
1754 // start the loop after items that are already sorted
1755 for (j=p; j<l; j=j+1) {
1757 // check the next module on the list to see if its
1758 // dependencies have been met
1761 // check everything below current item and move if we
1762 // find a requirement for the current item
1763 for (k=j+1; k<l; k=k+1) {
1765 if (!done[doneKey] && requires(a, s[k])) {
1767 // extract the dependency so we can move it up
1770 // insert the dependency above the item that
1772 s.splice(j, 0, b[0]);
1774 // only swap two dependencies once to short circut
1775 // circular dependencies
1776 done[doneKey] = true;
1785 // jump out of loop if we moved something
1788 // this item is sorted, move our pointer and keep going
1794 // when we make it here and moved is false, we are
1805 _insert: function(source, o, type) {
1808 // restore the state at the time of the request
1810 this._config(source);
1813 // build the dependency list
1814 this.calculate(o); // don't include type so we can process CSS and script in
1815 // one pass when the type is not specified.
1816 this.loadType = type;
1822 this._internalCallback = function() {
1825 f.call(self.context, Y);
1827 self._internalCallback = null;
1828 self._insert(null, null, JS);
1831 // _queue.running = false;
1832 this._insert(null, null, CSS);
1838 // set a flag to indicate the load has started
1839 this._loading = true;
1841 // flag to indicate we are done with the combo service
1842 // and any additional files will need to be loaded
1844 this._combineComplete = {};
1852 _continue: function() {
1853 if (!(_queue.running) && _queue.size() > 0) {
1854 _queue.running = true;
1860 * inserts the requested modules and their dependencies.
1861 * <code>type</code> can be "js" or "css". Both script and
1862 * css are inserted if type is not provided.
1864 * @param o optional options object
1865 * @param type {string} the type of dependency to insert
1867 insert: function(o, type) {
1870 var self = this, copy = Y.merge(this, true);
1872 delete copy.require;
1875 _queue.add(function() {
1876 self._insert(copy, o, type);
1884 * Executed every time a module is loaded, and if we are in a load
1885 * cycle, we attempt to load the next script. Public so that it
1886 * is possible to call this if using a method other than
1887 * Y.register to determine when scripts are fully loaded
1889 * @param mname {string} optional the name of the module that has
1890 * been loaded (which is usually why it is time to load the next
1893 loadNext: function(mname) {
1895 // It is possible that this function is executed due to something
1896 // else one the page loading a YUI module. Only react when we
1897 // are actively loading something
1898 if (!this._loading) {
1902 var s, len, i, m, url, self=this, type=this.loadType, fn, msg, attr,
1903 callback=function(o) {
1904 this._combineComplete[type] = true;
1906 var c=this._combining, len=c.length, i;
1908 for (i=0; i<len; i=i+1) {
1909 this.inserted[c[i]] = true;
1912 this.loadNext(o.data);
1914 onsuccess=function(o) {
1915 self.loadNext(o.data);
1918 // @TODO this will need to handle the two phase insert when
1919 // CSS support is added
1920 if (this.combine && (!this._combineComplete[type])) {
1922 this._combining = [];
1928 for (i=0; i<len; i=i+1) {
1929 m = this.getModule(s[i]);
1930 // Do not try to combine non-yui JS
1931 if (m && (m.type === type) && !m.ext) {
1932 url += this.root + m.path;
1937 this._combining.push(s[i]);
1941 if (this._combining.length) {
1944 // if (m.type === CSS) {
1947 attr = this.cssAttributes;
1950 attr = this.jsAttributes;
1953 // @TODO get rid of the redundant Get code
1954 fn(this._filter(url), {
1955 data: this._loading,
1956 onSuccess: callback,
1957 onFailure: this._onFailure,
1958 onTimeout: this._onTimeout,
1959 insertBefore: this.insertBefore,
1960 charset: this.charset,
1962 timeout: this.timeout,
1970 this._combineComplete[type] = true;
1976 // if the module that was just loaded isn't what we were expecting,
1978 if (mname !== this._loading) {
1983 // The global handler that is called when each module is loaded
1984 // will pass that module name to this function. Storing this
1985 // data to avoid loading the same module multiple times
1986 this.inserted[mname] = true;
1987 this.loaded[mname] = true;
1989 if (this.onProgress) {
1990 this.onProgress.call(this.context, {
2002 for (i=0; i<len; i=i+1) {
2004 // this.inserted keeps track of what the loader has loaded.
2005 // move on if this item is done.
2006 if (s[i] in this.inserted) {
2010 // Because rollups will cause multiple load notifications
2011 // from Y, loadNext may be called multiple times for
2012 // the same module when loading a rollup. We can safely
2013 // skip the subsequent requests
2014 if (s[i] === this._loading) {
2018 // log("inserting " + s[i]);
2019 m = this.getModule(s[i]);
2023 msg = "Undefined module " + s[i] + " skipped";
2024 this.inserted[s[i]] = true;
2025 this.skipped[s[i]] = true;
2031 // The load type is stored to offer the possibility to load
2032 // the css separately from the script.
2033 if (!type || type === m.type) {
2034 this._loading = s[i];
2036 if (m.type === CSS) {
2038 attr = this.cssAttributes;
2041 attr = this.jsAttributes;
2044 url = (m.fullpath) ? this._filter(m.fullpath, s[i]) : this._url(m.path, s[i]);
2048 onSuccess: onsuccess,
2049 insertBefore: this.insertBefore,
2050 charset: this.charset,
2052 onFailure: this._onFailure,
2053 onTimeout: this._onTimeout,
2054 timeout: this.timeout,
2064 this._loading = null;
2066 fn = this._internalCallback;
2068 // internal callback for loading css first
2070 this._internalCallback = null;
2073 // } else if (this.onSuccess) {
2075 // call Y.use passing this instance. Y will use the sorted
2083 * Apply filter defined for this instance to a url/path
2085 * @param u {string} the string to filter
2086 * @param name {string} the name of the module, if we are processing
2087 * a single module as opposed to a combined url
2088 * @return {string} the filtered string
2091 _filter: function(u, name) {
2093 var f = this.filter,
2094 hasFilter = name && (name in this.filters),
2095 modFilter = hasFilter && this.filters[name];
2100 f = (L.isString(modFilter)) ? this.FILTER_DEFS[modFilter.toUpperCase()] || null : modFilter;
2104 u = u.replace(new RegExp(f.searchExp, 'g'), f.replaceStr);
2113 * Generates the full url for a module
2115 * @param path {string} the path fragment
2116 * @return {string} the full url
2119 _url: function(path, name) {
2120 return this._filter((this.base || "") + path, name);