2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
10 * The YUI module contains the components required for building the YUI seed
11 * file. This includes the script loading mechanism, a simple queue, and
12 * the core utilities for the library.
17 if (typeof YUI != 'undefined') {
22 * The YUI global namespace object. If YUI is already defined, the
23 * existing YUI object will not be overwritten so that defined
24 * namespaces are preserved. It is the constructor for the object
25 * the end user interacts with. As indicated below, each instance
26 * has full custom event support, but only if the event system
27 * is available. This is a self-instantiable factory function. You
28 * can invoke it directly like this:
30 * YUI().use('*', function(Y) {
34 * But it also works like this:
42 * @param o* {object} 0..n optional configuration objects. these values
43 * are store in Y.config. See config for the list of supported
48 var YUI = function() {
53 instanceOf = function(o, type) {
54 return (o && o.hasOwnProperty && (o instanceof type));
56 gconf = (typeof YUI_config !== 'undefined') && YUI_config;
58 if (!(instanceOf(Y, YUI))) {
61 // set up the core environment
64 // YUI.GlobalConfig is a master configuration that might span
65 // multiple contexts in a non-browser environment. It is applied
66 // first to all instances in all contexts.
67 if (YUI.GlobalConfig) {
68 Y.applyConfig(YUI.GlobalConfig);
71 // YUI_Config is a page-level config. It is applied to all
72 // instances created on the page. This is applied after
73 // YUI.GlobalConfig, and before the instance level configuration
79 // bind the specified additional modules for this instance
86 // Each instance can accept one or more configuration objects.
87 // These are applied after YUI.GlobalConfig and YUI_Config,
88 // overriding values set in those config files if there is a '
91 Y.applyConfig(args[i]);
97 Y.instanceOf = instanceOf;
107 BASE = 'http://yui.yahooapis.com/',
108 DOC_LABEL = 'yui3-js-enabled',
109 NOOP = function() {},
110 SLICE = Array.prototype.slice,
111 APPLY_TO_AUTH = { 'io.xdrReady': 1, // the functions applyTo
112 'io.xdrResponse': 1, // can call. this should
113 'SWF.eventHandler': 1 }, // be done at build time
114 hasWin = (typeof window != 'undefined'),
115 win = (hasWin) ? window : null,
116 doc = (hasWin) ? win.document : null,
117 docEl = doc && doc.documentElement,
118 docClass = docEl && docEl.className,
120 time = new Date().getTime(),
121 add = function(el, type, fn, capture) {
122 if (el && el.addEventListener) {
123 el.addEventListener(type, fn, capture);
124 } else if (el && el.attachEvent) {
125 el.attachEvent('on' + type, fn);
128 remove = function(el, type, fn, capture) {
129 if (el && el.removeEventListener) {
130 // this can throw an uncaught exception in FF
132 el.removeEventListener(type, fn, capture);
134 } else if (el && el.detachEvent) {
135 el.detachEvent('on' + type, fn);
138 handleLoad = function() {
139 YUI.Env.windowLoaded = true;
140 YUI.Env.DOMReady = true;
142 remove(window, 'load', handleLoad);
145 getLoader = function(Y, o) {
146 var loader = Y.Env._loader;
148 loader.ignoreRegistered = false;
151 loader.required = [];
152 loader.loadType = null;
154 loader = new Y.Loader(Y.config);
155 Y.Env._loader = loader;
161 clobber = function(r, s) {
163 if (s.hasOwnProperty(i)) {
169 ALREADY_DONE = { success: true };
171 // Stamp the documentElement (HTML) with a class of "yui-loaded" to
172 // enable styles that need to key off of JS being enabled.
173 if (docEl && docClass.indexOf(DOC_LABEL) == -1) {
177 docClass += DOC_LABEL;
178 docEl.className = docClass;
181 if (VERSION.indexOf('@') > -1) {
182 VERSION = '3.2.0'; // dev time hack for cdn test
187 * Applies a new configuration object to the YUI instance config.
188 * This will merge new group/module definitions, and will also
189 * update the loader cache if necessary. Updating Y.config directly
190 * will not update the cache.
191 * @method applyConfig
192 * @param {object} the configuration object.
195 applyConfig: function(o) {
202 config = this.config,
203 mods = config.modules,
204 groups = config.groups,
206 loader = this.Env._loader;
209 if (o.hasOwnProperty(name)) {
211 if (mods && name == 'modules') {
213 } else if (groups && name == 'groups') {
214 clobber(groups, attr);
215 } else if (rls && name == 'rls') {
217 } else if (name == 'win') {
218 config[name] = attr.contentWindow || attr;
219 config.doc = config[name].document;
220 } else if (name == '_yuid') {
233 _config: function(o) {
238 * Initialize this YUI instance
249 * The version number of the YUI instance.
257 mods: {}, // flat module map
258 versions: {}, // version module map
260 cdn: BASE + VERSION + '/build/',
261 // bootstrapped: false,
270 getBase: G_ENV && G_ENV.getBase ||
272 function(srcPattern, comboPattern) {
273 var b, nodes, i, src, match;
274 // get from querystring
275 nodes = (doc && doc.getElementsByTagName('script')) || [];
276 for (i = 0; i < nodes.length; i = i + 1) {
280 match = src.match(srcPattern);
281 b = match && match[1];
283 // this is to set up the path to the loader. The file
284 // filter for loader should match the yui include.
288 match = filter.indexOf('js');
291 filter = filter.substr(0, match);
295 // extract correct path for mixed combo urls
296 // http://yuilibrary.com/projects/yui3/ticket/2528423
297 match = src.match(comboPattern);
298 if (match && match[3]) {
299 b = match[1] + match[3];
314 Env._loaded[VERSION] = {};
316 if (G_ENV && Y !== YUI) {
317 Env._yidx = ++G_ENV._yidx;
318 Env._guidp = ('yui_' + VERSION + '_' +
319 Env._yidx + '_' + time).replace(/\./g, '_');
320 } else if (YUI._YUI) {
322 G_ENV = YUI._YUI.Env;
323 Env._yidx += G_ENV._yidx;
324 Env._uidx += G_ENV._uidx;
326 for (prop in G_ENV) {
327 if (!(prop in Env)) {
328 Env[prop] = G_ENV[prop];
342 // configuration defaults
343 Y.config = Y.config || {
347 useBrowserConsole: true,
354 Y.config.base = YUI.config.base ||
355 Y.Env.getBase(/^(.*)yui\/yui([\.\-].*)js(\?.*)?$/,
356 /^(.*\?)(.*\&)(.*)yui\/yui[\.\-].*js(\?.*)?$/);
358 if (!filter || (!('-min.-debug.').indexOf(filter))) {
362 Y.config.loaderPath = YUI.config.loaderPath ||
363 'loader/loader' + (filter || '-min.') + 'js';
368 * Finishes the instance setup. Attaches whatever modules were defined
369 * when the yui modules was registered.
373 _setup: function(o) {
377 extras = Y.config.core || ['get',
385 for (i = 0; i < extras.length; i++) {
386 if (mods[extras[i]]) {
387 core.push(extras[i]);
391 Y._attach(['yui-base']);
397 * Executes a method on a YUI instance with
398 * the specified id if the specified method is whitelisted.
400 * @param id {string} the YUI instance id.
401 * @param method {string} the name of the method to exectute.
403 * @param args {Array} the arguments to apply to the method.
404 * @return {object} the return value from the applied method or null.
406 applyTo: function(id, method, args) {
407 if (!(method in APPLY_TO_AUTH)) {
408 this.log(method + ': applyTo not allowed', 'warn', 'yui');
412 var instance = instances[id], nest, m, i;
414 nest = method.split('.');
416 for (i = 0; i < nest.length; i = i + 1) {
419 this.log('applyTo not found: ' + method, 'warn', 'yui');
422 return m.apply(instance, args);
429 * Registers a module with the YUI global. The easiest way to create a
430 * first-class YUI module is to use the YUI component build tool.
432 * http://yuilibrary.com/projects/builder
434 * The build system will produce the YUI.add wrapper for you module, along
435 * with any configuration info required for the module.
437 * @param name {string} module name.
438 * @param fn {Function} entry point into the module that
439 * is used to bind module to the YUI instance.
440 * @param version {string} version string.
441 * @param details {object} optional config data:
442 * requires: features that must be present before this module can be
444 * optional: optional features that should be present if loadOptional
445 * is defined. Note: modules are not often loaded this way in YUI 3,
446 * but this field is still useful to inform the user that certain
447 * features in the component will require additional dependencies.
448 * use: features that are included within this module which need to
449 * be attached automatically when this module is attached. This
450 * supports the YUI 3 rollup system -- a module with submodules
451 * defined will need to have the submodules listed in the 'use'
452 * config. The YUI component build tool does this for you.
453 * @return {YUI} the YUI instance.
456 add: function(name, fn, version, details) {
457 details = details || {};
466 i, versions = env.versions;
468 env.mods[name] = mod;
469 versions[version] = versions[version] || {};
470 versions[version][name] = mod;
472 for (i in instances) {
473 if (instances.hasOwnProperty(i)) {
474 loader = instances[i].Env._loader;
476 if (!loader.moduleInfo[name]) {
477 loader.addModule(details, name);
487 * Executes the function associated with each required
488 * module, binding the module to the YUI instance.
492 _attach: function(r, fromLoader) {
493 var i, name, mod, details, req, use, after,
496 done = Y.Env._attached,
497 len = r.length, loader;
500 for (i = 0; i < len; i++) {
505 loader = Y.Env._loader;
508 if (!loader || !loader.moduleInfo[name]) {
509 Y.message('NOT loaded: ' + name, 'warn', 'yui');
513 details = mod.details;
514 req = details.requires;
516 after = details.after;
519 for (j = 0; j < req.length; j++) {
521 if (!Y._attach(req)) {
530 for (j = 0; j < after.length; j++) {
531 if (!done[after[j]]) {
532 if (!Y._attach(after)) {
541 for (j = 0; j < use.length; j++) {
543 if (!Y._attach(use)) {
555 Y.error('Attach error: ' + name, e, name);
568 * Attaches one or more modules to the YUI instance. When this
569 * is executed, the requirements are analyzed, and one of
570 * several things can happen:
572 * - All requirements are available on the page -- The modules
573 * are attached to the instance. If supplied, the use callback
574 * is executed synchronously.
576 * - Modules are missing, the Get utility is not available OR
577 * the 'bootstrap' config is false -- A warning is issued about
578 * the missing modules and all available modules are attached.
580 * - Modules are missing, the Loader is not available but the Get
581 * utility is and boostrap is not false -- The loader is bootstrapped
582 * before doing the following....
584 * - Modules are missing and the Loader is available -- The loader
585 * expands the dependency tree and fetches missing modules. When
586 * the loader is finshed the callback supplied to use is executed
589 * @param modules* {string} 1-n modules to bind (uses arguments array).
590 * @param *callback {function} callback function executed when
591 * the instance has the required functionality. If included, it
592 * must be the last parameter.
594 * // loads and attaches drag and drop and its dependencies
595 * YUI().use('dd', function(Y) {});
596 * // attaches all modules that are available on the page
597 * YUI().use('*', function(Y) {});
598 * // intrinsic YUI gallery support (since 3.1.0)
599 * YUI().use('gallery-yql', function(Y) {});
600 * // intrinsic YUI 2in3 support (since 3.1.0)
601 * YUI().use('yui2-datatable', function(Y) {});.
604 * @return {YUI} the YUI instance.
607 var args = SLICE.call(arguments, 0),
608 callback = args[args.length - 1],
612 // The last argument supplied to use can be a load complete callback
613 if (Y.Lang.isFunction(callback)) {
620 Y._useQueue = Y._useQueue || new Y.Queue();
621 Y._useQueue.add([args, callback]);
625 if (Y.config.cacheUse && Y.Env.serviced[key]) {
626 Y._notify(callback, ALREADY_DONE, args);
628 Y._use(args, function(Y, response) {
629 if (Y.config.cacheUse) {
630 Y.Env.serviced[key] = true;
632 Y._notify(callback, response, args);
640 _notify: function(callback, response, args) {
641 if (!response.success && this.config.loadErrorFn) {
642 this.config.loadErrorFn.call(this, this, callback, response, args);
643 } else if (callback) {
645 callback(this, response);
647 this.error('use callback error', e, args);
652 _use: function(args, callback) {
655 this._attach(['yui-base']);
658 var len, loader, handleBoot,
664 queue = G_ENV._loaderQueue,
668 boot = config.bootstrap,
672 fetchCSS = config.fetchCSS,
673 process = function(names, skip) {
679 YArray.each(names, function(name) {
681 // add this module to full list of things to attach
686 // only attach a module once
691 var m = mods[name], req, use;
695 req = m.details.requires;
698 // CSS files don't register themselves, see if it has
700 if (!G_ENV._loaded[VERSION][name]) {
703 used[name] = true; // probably css
707 // make sure requirements are attached
708 if (req && req.length) {
712 // make sure we grab the submodule dependencies too
713 if (use && use.length) {
719 handleLoader = function(fromLoader) {
720 var response = fromLoader || {
726 data = response.data;
732 origMissing = missing;
736 redo = missing.length;
738 if (missing.sort().join() ==
739 origMissing.sort().join()) {
747 Y._use(args, function() {
748 if (Y._attach(data)) {
749 Y._notify(callback, response, data);
754 ret = Y._attach(data);
757 Y._notify(callback, response, args);
761 if (Y._useQueue && Y._useQueue.size() && !Y._loading) {
762 Y._use.apply(Y, Y._useQueue.next());
768 // YUI().use('*'); // bind everything available
769 if (firstArg === '*') {
770 ret = Y._attach(Y.Object.keys(mods));
778 // use loader to expand dependencies and sort the
779 // requirements if it is available.
780 if (boot && Y.Loader && args.length) {
781 loader = getLoader(Y);
782 loader.require(args);
783 loader.ignoreRegistered = true;
784 loader.calculate(null, (fetchCSS) ? null : 'js');
785 args = loader.sorted;
788 // process each requirement and any additional requirements
789 // the module metadata specifies
792 len = missing.length;
795 missing = Y.Object.keys(YArray.hash(missing));
796 len = missing.length;
800 if (boot && len && Y.Loader) {
802 loader = getLoader(Y);
803 loader.onEnd = handleLoader;
806 loader.ignoreRegistered = false;
807 loader.require(args);
808 loader.insert(null, (fetchCSS) ? null : 'js');
809 // loader.partial(missing, (fetchCSS) ? null : 'js');
811 } else if (len && Y.config.use_rls) {
813 // server side loader service
814 Y.Get.script(Y._rls(args), {
821 } else if (boot && len && Y.Get && !Env.bootstrapped) {
825 handleBoot = function() {
827 queue.running = false;
828 Env.bootstrapped = true;
829 if (Y._attach(['loader'])) {
830 Y._use(args, callback);
834 if (G_ENV._bootstrapping) {
835 queue.add(handleBoot);
837 G_ENV._bootstrapping = true;
838 Y.Get.script(config.base + config.loaderPath, {
844 ret = Y._attach(args);
855 * Returns the namespace specified and creates it if it doesn't exist
857 * YUI.namespace("property.package");
858 * YUI.namespace("YAHOO.property.package");
860 * Either of the above would create YUI.property, then
861 * YUI.property.package (YAHOO is scrubbed out, this is
862 * to remain compatible with YUI2)
864 * Be careful when naming packages. Reserved words may work in some browsers
865 * and not others. For instance, the following will fail in Safari:
867 * YUI.namespace("really.long.nested.namespace");
869 * This fails because "long" is a future reserved word in ECMAScript
872 * @param {string*} arguments 1-n namespaces to create.
873 * @return {object} A reference to the last namespace object created.
875 namespace: function() {
876 var a = arguments, o = this, i = 0, j, d, arg;
877 for (; i < a.length; i++) {
878 // d = ('' + a[i]).split('.');
880 if (arg.indexOf(PERIOD)) {
881 d = arg.split(PERIOD);
882 for (j = (d[0] == 'YAHOO') ? 1 : 0; j < d.length; j++) {
883 o[d[j]] = o[d[j]] || {};
887 o[arg] = o[arg] || {};
893 // this is replaced if the log module is included
898 * Report an error. The reporting mechanism is controled by
899 * the 'throwFail' configuration attribute. If throwFail is
900 * not specified, the message is written to the Logger, otherwise
901 * a JS error is thrown
903 * @param msg {string} the error message.
904 * @param e {Error|string} Optional JS error that was caught, or an error string.
905 * @param data Optional additional info
906 * and throwFail is specified, this error will be re-thrown.
907 * @return {YUI} this YUI instance.
909 error: function(msg, e, data) {
913 if (Y.config.errorFn) {
914 ret = Y.config.errorFn.apply(Y, arguments);
917 if (Y.config.throwFail && !ret) {
918 throw (e || new Error(msg));
920 Y.message(msg, 'error'); // don't scrub this one
927 * Generate an id that is unique among all YUI instances
929 * @param pre {string} optional guid prefix.
930 * @return {string} the guid.
932 guid: function(pre) {
933 var id = this.Env._guidp + (++this.Env._uidx);
934 return (pre) ? (pre + id) : id;
938 * Returns a guid associated with an object. If the object
939 * does not have one, a new one is created unless readOnly
942 * @param o The object to stamp.
943 * @param readOnly {boolean} if true, a valid guid will only
944 * be returned if the object has one assigned to it.
945 * @return {string} The object's guid or null.
947 stamp: function(o, readOnly) {
953 // IE generates its own unique ID for dom nodes
954 // The uniqueID property of a document node returns a new ID
955 if (o.uniqueID && o.nodeType && o.nodeType !== 9) {
958 uid = (typeof o === 'string') ? o : o._yuid;
975 * Destroys the YUI instance
979 destroy: function() {
984 delete instances[Y.id];
990 * instanceof check for objects that works around
991 * memory leak in IE when the item tested is
1000 YUI.prototype = proto;
1002 // inheritance utilities are not available yet
1003 for (prop in proto) {
1004 if (proto.hasOwnProperty(prop)) {
1005 YUI[prop] = proto[prop];
1009 // set up the environment
1013 // add a window load event at load time so we can capture
1014 // the case where it fires before dynamic loading is
1016 add(window, 'load', handleLoad);
1022 YUI.Env.remove = remove;
1025 // Support the CommonJS method for exporting our single global
1026 if (typeof exports == 'object') {
1034 * The config object contains all of the configuration options for
1035 * the YUI instance. This object is supplied by the implementer
1036 * when instantiating a YUI instance. Some properties have default
1037 * values if they are not supplied by the implementer. This should
1038 * not be updated directly because some values are cached. Use
1039 * applyConfig() to update the config object on a YUI instance that
1040 * has already been configured.
1047 * Allows the YUI seed file to fetch the loader component and library
1048 * metadata to dynamically load additional dependencies.
1050 * @property bootstrap
1056 * Log to the browser console if debug is on and the browser has a
1057 * supported console.
1059 * @property useBrowserConsole
1065 * A hash of log sources that should be logged. If specified, only
1066 * log messages from these sources will be logged.
1068 * @property logInclude
1073 * A hash of log sources that should be not be logged. If specified,
1074 * all sources are logged if not on this list.
1076 * @property logExclude
1081 * Set to true if the yui seed file was dynamically loaded in
1082 * order to bootstrap components relying on the window load event
1083 * and the 'domready' custom event.
1085 * @property injected
1091 * If throwFail is set, Y.error will generate or re-throw a JS Error.
1092 * Otherwise the failure is logged.
1094 * @property throwFail
1100 * The window/frame that this instance should operate in.
1104 * @default the window hosting YUI
1108 * The document associated with the 'win' configuration.
1112 * @default the document hosting YUI
1116 * A list of modules that defines the YUI core (overrides the default).
1123 * A list of languages in order of preference. This list is matched against
1124 * the list of available languages in modules that the YUI instance uses to
1125 * determine the best possible localization of language sensitive modules.
1126 * Languages are represented using BCP 47 language tags, such as "en-GB" for
1127 * English as used in the United Kingdom, or "zh-Hans-CN" for simplified
1128 * Chinese as used in China. The list can be provided as a comma-separated
1129 * list or as an array.
1132 * @type string|string[]
1136 * The default date format
1137 * @property dateFormat
1139 * @deprecated use configuration in DataType.Date.format() instead.
1143 * The default locale
1146 * @deprecated use config.lang instead.
1150 * The default interval when polling in milliseconds.
1151 * @property pollInterval
1157 * The number of dynamic nodes to insert by default before
1158 * automatically removing them. This applies to script nodes
1159 * because remove the node will not make the evaluated script
1160 * unavailable. Dynamic CSS is not auto purged, because removing
1161 * a linked style sheet will also remove the style definitions.
1162 * @property purgethreshold
1168 * The default interval when polling in milliseconds.
1169 * @property windowResizeDelay
1175 * Base directory for dynamic loading
1181 * The secure base dir (not implemented)
1182 * For dynamic loading.
1183 * @property secureBase
1188 * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
1189 * For dynamic loading.
1190 * @property comboBase
1195 * The root path to prepend to module path for the combo service.
1196 * Ex: 3.0.0b1/build/
1197 * For dynamic loading.
1203 * A filter to apply to result urls. This filter will modify the default
1204 * path for all modules. The default path for the YUI library is the
1205 * minified version of the files (e.g., event-min.js). The filter property
1206 * can be a predefined filter or a custom filter. The valid predefined
1210 * <dd>Selects the debug versions of the library (e.g., event-debug.js).
1211 * This option will automatically include the Logger widget</dd>
1213 * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
1215 * You can also define a custom filter, which must be an object literal
1216 * containing a search expression and a replace string:
1219 * 'searchExp': "-min\\.js",
1220 * 'replaceStr': "-debug.js"
1224 * For dynamic loading.
1227 * @type string|object
1231 * The 'skin' config let's you configure application level skin
1232 * customizations. It contains the following attributes which
1233 * can be specified to override the defaults:
1235 * // The default skin, which is automatically applied if not
1236 * // overriden by a component-specific skin definition.
1237 * // Change this in to apply a different skin globally
1238 * defaultSkin: 'sam',
1240 * // This is combined with the loader base property to get
1241 * // the default root directory for a skin.
1242 * base: 'assets/skins/',
1244 * // Any component-specific overrides can be specified here,
1245 * // making it possible to load different skins for different
1246 * // components. It is possible to load more than one skin
1247 * // for a given component as well.
1249 * slider: ['capsule', 'round']
1252 * For dynamic loading.
1258 * Hash of per-component filter specification. If specified for a given
1259 * component, this overrides the filter config.
1261 * For dynamic loading.
1267 * Use the YUI combo service to reduce the number of http connections
1268 * required to load your dependencies. Turning this off will
1269 * disable combo handling for YUI and all module groups configured
1270 * with a combo service.
1272 * For dynamic loading.
1276 * @default true if 'base' is not supplied, false if it is.
1280 * A list of modules that should never be dynamically loaded
1287 * A list of modules that should always be loaded when required, even if already
1288 * present on the page.
1295 * Node or id for a node that should be used as the insertion point for new
1296 * nodes. For dynamic loading.
1298 * @property insertBefore
1303 * Object literal containing attributes to add to dynamically loaded script
1305 * @property jsAttributes
1310 * Object literal containing attributes to add to dynamically loaded link
1312 * @property cssAttributes
1317 * Number of milliseconds before a timeout occurs when dynamically
1318 * loading nodes. If not set, there is no timeout.
1324 * Callback for the 'CSSComplete' event. When dynamically loading YUI
1325 * components with CSS, this property fires when the CSS is finished
1326 * loading but script loading is still ongoing. This provides an
1327 * opportunity to enhance the presentation of a loading page a little
1328 * bit before the entire loading process is done.
1335 * A hash of module definitions to add to the list of YUI components.
1336 * These components can then be dynamically loaded side by side with
1337 * YUI via the use() method. This is a hash, the key is the module
1338 * name, and the value is an object literal specifying the metdata
1339 * for the module. * See Loader.addModule for the supported module
1340 * metadata fields. Also @see groups, which provides a way to
1341 * configure the base and combo spec for a set of modules.
1345 * requires: ['node'],
1346 * fullpath: 'http://myserver.mydomain.com/mymod1/mymod1.js'
1349 * requires: ['mymod1'],
1350 * fullpath: 'http://myserver.mydomain.com/mymod2/mymod2.js'
1360 * A hash of module group definitions. It for each group you
1361 * can specify a list of modules and the base path and
1362 * combo spec to use when dynamically loading the modules. @see
1363 * @see modules for the details about the modules part of the
1368 * // specify whether or not this group has a combo service
1369 * combine: true,
1371 * // the base path for non-combo paths
1372 * base: 'http://yui.yahooapis.com/2.8.0r4/build/',
1374 * // the path to the combo service
1375 * comboBase: 'http://yui.yahooapis.com/combo?',
1377 * // a fragment to prepend to the path attribute when
1378 * // when building combo urls
1379 * root: '2.8.0r4/build/',
1381 * // the module definitions
1383 * yui2_yde: {
1384 * path: "yahoo-dom-event/yahoo-dom-event.js"
1386 * yui2_anim: {
1387 * path: "animation/animation.js",
1388 * requires: ['yui2_yde']
1399 * The loader 'path' attribute to the loader itself. This is combined
1400 * with the 'base' attribute to dynamically load the loader component
1401 * when boostrapping with the get utility alone.
1403 * @property loaderPath
1405 * @default loader/loader-min.js
1409 * Specifies whether or not YUI().use(...) will attempt to load CSS
1410 * resources at all. Any truthy value will cause CSS dependencies
1411 * to load when fetching script. The special value 'force' will
1412 * cause CSS dependencies to be loaded even if no script is needed.
1414 * @property fetchCSS
1415 * @type boolean|string
1420 * The default gallery version to build gallery module urls
1427 * The default YUI 2 version to build yui2 module urls. This is for
1428 * intrinsic YUI 2 support via the 2in3 project. Also @see the '2in3'
1429 * config for pulling different revisions of the wrapped YUI 2
1438 * The 2in3 project is a deployment of the various versions of YUI 2
1439 * deployed as first-class YUI 3 modules. Eventually, the wrapper
1440 * for the modules will change (but the underlying YUI 2 code will
1441 * be the same), and you can select a particular version of
1442 * the wrapper modules via this config.
1450 * Alternative console log function for use in environments without
1451 * a supported native console. The function is executed in the
1452 * YUI instance context.
1459 * A callback to execute when Y.error is called. It receives the
1460 * error message and an javascript error object if Y.error was
1461 * executed because a javascript error was caught. The function
1462 * is executed in the YUI instance context.
1470 * A callback to execute when the loader fails to load one or
1471 * more resource. This could be because of a script load
1472 * failure. It can also fail if a javascript module fails
1473 * to register itself, but only when the 'requireRegistration'
1474 * is true. If this function is defined, the use() callback will
1475 * only be called when the loader succeeds, otherwise it always
1476 * executes unless there was a javascript error when attaching
1480 * @property loadErrorFn
1485 * When set to true, the YUI loader will expect that all modules
1486 * it is responsible for loading will be first-class YUI modules
1487 * that register themselves with the YUI global. If this is
1488 * set to true, loader will fail if the module registration fails
1489 * to happen after the script is loaded.
1492 * @property requireRegistration
1498 * Cache serviced use() requests.
1500 * @property cacheUse
1506 * The parameter defaults for the remote loader service.
1507 * Requires the rls submodule. The properties that are
1510 * m: comma separated list of module requirements. This
1511 * must be the param name even for custom implemetations.
1512 * v: the version of YUI to load. Defaults to the version
1513 * of YUI that is being used.
1514 * gv: the version of the gallery to load (@see the gallery config)
1515 * env: comma separated list of modules already on the page.
1516 * this must be the param name even for custom implemetations.
1517 * lang: the languages supported on the page (@see the lang config)
1518 * '2in3v': the version of the 2in3 wrapper to use (@see the 2in3 config).
1519 * '2v': the version of yui2 to use in the yui 2in3 wrappers
1520 * (@see the yui2 config)
1521 * filt: a filter def to apply to the urls (@see the filter config).
1522 * filts: a list of custom filters to apply per module
1523 * (@see the filters config).
1524 * tests: this is a map of conditional module test function id keys
1525 * with the values of 1 if the test passes, 0 if not. This must be
1526 * the name of the querystring param in custom templates.
1534 * The base path to the remote loader service
1537 * @property rls_base
1541 * The template to use for building the querystring portion
1542 * of the remote loader service url. The default is determined
1543 * by the rls config -- each property that has a value will be
1546 * ex: m={m}&v={v}&env={env}&lang={lang}&filt={filt}&tests={tests}
1550 * @property rls_tmpl
1554 * Configure the instance to use a remote loader service instead of
1555 * the client loader.
1560 YUI.add('yui-base', function(Y) {
1565 * @submodule yui-base
1568 * The YUI module contains the components required for building the YUI
1569 * seed file. This includes the script loading mechanism, a simple queue,
1570 * and the core utilities for the library.
1572 * @submodule yui-base
1576 * Provides the language utilites and extensions used by the library
1580 Y.Lang = Y.Lang || {};
1585 BOOLEAN = 'boolean',
1588 FUNCTION = 'function',
1594 STRING_PROTO = String.prototype,
1595 TOSTRING = Object.prototype.toString,
1596 UNDEFINED = 'undefined',
1599 'undefined' : UNDEFINED,
1601 'boolean' : BOOLEAN,
1603 '[object Function]' : FUNCTION,
1604 '[object RegExp]' : REGEX,
1605 '[object Array]' : ARRAY,
1606 '[object Date]' : DATE,
1607 '[object Error]' : ERROR
1610 TRIMREGEX = /^\s+|\s+$/g,
1612 SUBREGEX = /\{\s*([^\|\}]+?)\s*(?:\|([^\}]*))?\s*\}/g;
1615 * Determines whether or not the provided item is an array.
1616 * Returns false for array-like collections such as the
1617 * function arguments collection or HTMLElement collection
1618 * will return false. Use <code>Y.Array.test</code> if you
1619 * want to test for an array-like collection.
1622 * @param o The object to test.
1623 * @return {boolean} true if o is an array.
1625 // L.isArray = Array.isArray || function(o) {
1626 // return L.type(o) === ARRAY;
1629 L.isArray = function(o) {
1630 return L.type(o) === ARRAY;
1634 * Determines whether or not the provided item is a boolean.
1637 * @param o The object to test.
1638 * @return {boolean} true if o is a boolean.
1640 L.isBoolean = function(o) {
1641 return typeof o === BOOLEAN;
1646 * Determines whether or not the provided item is a function.
1647 * Note: Internet Explorer thinks certain functions are objects:
1651 * var obj = document.createElement("object");
1652 * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
1654 * var input = document.createElement("input"); // append to body
1655 * Y.Lang.isFunction(input.focus) // reports false in IE
1659 * You will have to implement additional tests if these functions
1663 * @method isFunction
1665 * @param o The object to test.
1666 * @return {boolean} true if o is a function.
1668 L.isFunction = function(o) {
1669 return L.type(o) === FUNCTION;
1673 * Determines whether or not the supplied item is a date instance.
1676 * @param o The object to test.
1677 * @return {boolean} true if o is a date.
1679 L.isDate = function(o) {
1680 // return o instanceof Date;
1681 return L.type(o) === DATE && o.toString() !== 'Invalid Date' && !isNaN(o);
1685 * Determines whether or not the provided item is null.
1688 * @param o The object to test.
1689 * @return {boolean} true if o is null.
1691 L.isNull = function(o) {
1696 * Determines whether or not the provided item is a legal number.
1699 * @param o The object to test.
1700 * @return {boolean} true if o is a number.
1702 L.isNumber = function(o) {
1703 return typeof o === NUMBER && isFinite(o);
1707 * Determines whether or not the provided item is of type object
1708 * or function. Note that arrays are also objects, so
1709 * <code>Y.Lang.isObject([]) === true</code>.
1712 * @param o The object to test.
1713 * @param failfn {boolean} fail if the input is a function.
1714 * @return {boolean} true if o is an object.
1716 L.isObject = function(o, failfn) {
1718 return (o && (t === OBJECT ||
1719 (!failfn && (t === FUNCTION || L.isFunction(o))))) || false;
1723 * Determines whether or not the provided item is a string.
1726 * @param o The object to test.
1727 * @return {boolean} true if o is a string.
1729 L.isString = function(o) {
1730 return typeof o === STRING;
1734 * Determines whether or not the provided item is undefined.
1735 * @method isUndefined
1737 * @param o The object to test.
1738 * @return {boolean} true if o is undefined.
1740 L.isUndefined = function(o) {
1741 return typeof o === UNDEFINED;
1745 * Returns a string without any leading or trailing whitespace. If
1746 * the input is not a string, the input will be returned untouched.
1749 * @param s {string} the string to trim.
1750 * @return {string} the trimmed string.
1752 L.trim = STRING_PROTO.trim ? function(s) {
1753 return (s && s.trim) ? s.trim() : s;
1756 return s.replace(TRIMREGEX, EMPTYSTRING);
1763 * Returns a string without any leading whitespace.
1766 * @param s {string} the string to trim.
1767 * @return {string} the trimmed string.
1769 L.trimLeft = STRING_PROTO.trimLeft ? function (s) {
1770 return s.trimLeft();
1772 return s.replace(/^\s+/, '');
1776 * Returns a string without any trailing whitespace.
1779 * @param s {string} the string to trim.
1780 * @return {string} the trimmed string.
1782 L.trimRight = STRING_PROTO.trimRight ? function (s) {
1783 return s.trimRight();
1785 return s.replace(/\s+$/, '');
1789 * A convenience method for detecting a legitimate non-null value.
1790 * Returns false for null/undefined/NaN, true for other values,
1791 * including 0/false/''
1794 * @param o The item to test.
1795 * @return {boolean} true if it is not null/undefined/NaN || false.
1797 L.isValue = function(o) {
1812 * Returns a string representing the type of the item passed in.
1821 * <code>typeof HTMLElementCollection</code> returns function in Safari, but
1822 * <code>Y.type()</code> reports object, which could be a good thing --
1823 * but it actually caused the logic in <code>Y.Lang.isObject</code> to fail.
1828 * @param o the item to test.
1829 * @return {string} the detected type.
1832 L.type = function(o) {
1833 return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
1837 * Lightweight version of <code>Y.substitute</code>. Uses the same template
1838 * structure as <code>Y.substitute</code>, but doesn't support recursion,
1839 * auto-object coersion, or formats.
1841 * @param {string} s String to be modified.
1842 * @param {object} o Object containing replacement values.
1843 * @return {string} the substitute result.
1847 L.sub = function(s, o) {
1848 return ((s.replace) ? s.replace(SUBREGEX, function(match, key) {
1849 return (!L.isUndefined(o[key])) ? o[key] : match;
1854 * Returns the current time in milliseconds.
1856 * @return {int} the current date
1859 L.now = Date.now || function () {
1860 return new Date().getTime();
1864 * The YUI module contains the components required for building the YUI seed
1865 * file. This includes the script loading mechanism, a simple queue, and the
1866 * core utilities for the library.
1868 * @submodule yui-base
1872 var Native = Array.prototype, LENGTH = 'length',
1875 * Adds the following array utilities to the YUI instance. Additional
1876 * array helpers can be found in the collection component.
1881 * Y.Array(o) returns an array:
1882 * - Arrays are return unmodified unless the start position is specified.
1883 * - "Array-like" collections (@see Array.test) are converted to arrays
1884 * - For everything else, a new array is created with the input as the sole
1886 * - The start position is used if the input is or is like an array to return
1887 * a subset of the collection.
1889 * @todo this will not automatically convert elements that are also
1890 * collections such as forms and selects. Passing true as the third
1891 * param will force a conversion.
1895 * @param {object} o the item to arrayify.
1896 * @param {int} startIdx if an array or array-like, this is the start index.
1897 * @param {boolean} arraylike if true, it forces the array-like fork. This
1898 * can be used to avoid multiple Array.test calls.
1899 * @return {Array} the resulting array.
1901 YArray = function(o, startIdx, arraylike) {
1902 var t = (arraylike) ? 2 : YArray.test(o),
1903 l, a, start = startIdx || 0;
1906 // IE errors when trying to slice HTMLElement collections
1908 return Native.slice.call(o, start);
1912 for (; start < l; start++) {
1925 * Evaluates the input to determine if it is an array, array-like, or
1926 * something else. This is used to handle the arguments collection
1927 * available within functions, and HTMLElement collections
1932 * @todo current implementation (intenionally) will not implicitly
1933 * handle html elements that are array-like (forms, selects, etc).
1935 * @param {object} o the object to test.
1937 * @return {int} a number indicating the results:
1938 * 0: Not an array or an array-like collection
1940 * 2: array-like collection.
1942 YArray.test = function(o) {
1944 if (Y.Lang.isObject(o)) {
1945 if (Y.Lang.isArray(o)) {
1949 // indexed, but no tagName (element) or alert (window),
1950 // or functions without apply/call (Safari
1951 // HTMLElementCollection bug).
1952 if ((LENGTH in o) && !o.tagName && !o.alert && !o.apply) {
1963 * Executes the supplied function on each item in the array.
1965 * @param {Array} a the array to iterate.
1966 * @param {Function} f the function to execute on each item. The
1967 * function receives three arguments: the value, the index, the full array.
1968 * @param {object} o Optional context object.
1970 * @return {YUI} the YUI instance.
1972 YArray.each = (Native.forEach) ?
1974 Native.forEach.call(a || [], f, o || Y);
1978 var l = (a && a.length) || 0, i;
1979 for (i = 0; i < l; i = i + 1) {
1980 f.call(o || Y, a[i], i, a);
1986 * Returns an object using the first array as keys, and
1987 * the second as values. If the second array is not
1988 * provided the value is set to true for each.
1991 * @param {Array} k keyset.
1992 * @param {Array} v optional valueset.
1993 * @return {object} the hash.
1995 YArray.hash = function(k, v) {
1996 var o = {}, l = k.length, vl = v && v.length, i;
1997 for (i = 0; i < l; i = i + 1) {
1998 o[k[i]] = (vl && vl > i) ? v[i] : true;
2005 * Returns the index of the first item in the array
2006 * that contains the specified value, -1 if the
2007 * value isn't found.
2010 * @param {Array} a the array to search.
2011 * @param {any} val the value to search for.
2012 * @return {int} the index of the item that contains the value or -1.
2014 YArray.indexOf = (Native.indexOf) ?
2016 return Native.indexOf.call(a, val);
2019 for (var i = 0; i < a.length; i = i + 1) {
2029 * Numeric sort convenience function.
2030 * Y.ArrayAssert.itemsAreEqual([1,2,3], [3,1,2].sort(Y.Array.numericSort));
2031 * @method numericSort
2033 * @param {number} a a number.
2034 * @param {number} b a number.
2036 YArray.numericSort = function(a, b) {
2041 * Executes the supplied function on each item in the array.
2042 * Returning true from the processing function will stop the
2043 * processing of the remaining items.
2045 * @param {Array} a the array to iterate.
2046 * @param {Function} f the function to execute on each item. The function
2047 * receives three arguments: the value, the index, the full array.
2048 * @param {object} o Optional context object.
2050 * @return {boolean} true if the function returns true on
2051 * any of the items in the array.
2053 YArray.some = (Native.some) ?
2055 return Native.some.call(a, f, o);
2058 var l = a.length, i;
2059 for (i = 0; i < l; i = i + 1) {
2060 if (f.call(o, a[i], i, a)) {
2068 * The YUI module contains the components required for building the YUI
2069 * seed file. This includes the script loading mechanism, a simple queue,
2070 * and the core utilities for the library.
2072 * @submodule yui-base
2076 * A simple FIFO queue. Items are added to the Queue with add(1..n items) and
2077 * removed using next().
2081 * @param {MIXED} item* 0..n items to seed the queue.
2085 this.add.apply(this, arguments);
2090 * Initialize the queue
2097 * The collection of enqueued items
2107 * Get the next item in the queue. FIFO support
2110 * @return {MIXED} the next item in the queue.
2113 return this._q.shift();
2117 * Get the last in the queue. LIFO support.
2120 * @return {MIXED} the last item in the queue.
2123 return this._q.pop();
2127 * Add 0..n items to the end of the queue.
2130 * @param {MIXED} item* 0..n items.
2131 * @return {object} this queue.
2134 this._q.push.apply(this._q, arguments);
2140 * Returns the current number of queued items.
2143 * @return {Number} The size.
2146 return this._q.length;
2152 YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Queue();
2155 * The YUI module contains the components required for building the YUI
2156 * seed file. This includes the script loading mechanism, a simple queue,
2157 * and the core utilities for the library.
2159 * @submodule yui-base
2162 var CACHED_DELIMITER = '__',
2165 * IE will not enumerate native functions in a derived object even if the
2166 * function was overridden. This is a workaround for specific functions
2167 * we care about on the Object prototype.
2170 * @param {Function} r the object to receive the augmentation
2171 * @param {Function} s the object that supplies the properties to augment
2174 _iefix = function(r, s) {
2175 var fn = s.toString;
2176 if (Y.Lang.isFunction(fn) && fn != Object.prototype.toString) {
2183 * Returns a new object containing all of the properties of
2184 * all the supplied objects. The properties from later objects
2185 * will overwrite those in earlier objects. Passing in a
2186 * single object will create a shallow copy of it. For a deep
2190 * @param arguments {Object*} the objects to merge.
2191 * @return {object} the new merged object.
2193 Y.merge = function() {
2194 var a = arguments, o = {}, i, l = a.length;
2195 for (i = 0; i < l; i = i + 1) {
2196 Y.mix(o, a[i], true);
2202 * Applies the supplier's properties to the receiver. By default
2203 * all prototype and static propertes on the supplier are applied
2204 * to the corresponding spot on the receiver. By default all
2205 * properties are applied, and a property that is already on the
2206 * reciever will not be overwritten. The default behavior can
2207 * be modified by supplying the appropriate parameters.
2209 * @todo add constants for the modes
2212 * @param {Function} r the object to receive the augmentation.
2213 * @param {Function} s the object that supplies the properties to augment.
2214 * @param ov {boolean} if true, properties already on the receiver
2215 * will be overwritten if found on the supplier.
2216 * @param wl {string[]} a whitelist. If supplied, only properties in
2217 * this list will be applied to the receiver.
2218 * @param {int} mode what should be copies, and to where
2219 * default(0): object to object
2220 * 1: prototype to prototype (old augment)
2221 * 2: prototype to prototype and object props (new augment)
2222 * 3: prototype to object
2223 * 4: object to prototype.
2224 * @param merge {boolean/int} merge objects instead of overwriting/ignoring.
2225 * A value of 2 will skip array merge
2226 * Used by Y.aggregate.
2227 * @return {object} the augmented object.
2229 Y.mix = function(r, s, ov, wl, mode, merge) {
2237 case 1: // proto to proto
2238 return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
2239 case 2: // object to object and proto to proto
2240 Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
2241 break; // pass through
2242 case 3: // proto to static
2243 return Y.mix(r, s.prototype, ov, wl, 0, merge);
2244 case 4: // static to proto
2245 return Y.mix(r.prototype, s, ov, wl, 0, merge);
2246 default: // object to object is what happens below
2250 // Maybe don't even need this wl && wl.length check anymore??
2253 if (wl && wl.length) {
2254 for (i = 0, l = wl.length; i < l; ++i) {
2256 type = Y.Lang.type(r[p]);
2257 if (s.hasOwnProperty(p)) {
2258 if (merge && type == 'object') {
2260 } else if (ov || !(p in r)) {
2267 // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
2268 if (s.hasOwnProperty(i)) {
2269 // check white list if it was supplied
2270 // if the receiver has this property, it is an object,
2271 // and merge is specified, merge the two objects.
2272 if (merge && Y.Lang.isObject(r[i], true)) {
2273 Y.mix(r[i], s[i], ov, wl, 0, true); // recursive
2274 // otherwise apply the property only if overwrite
2275 // is specified or the receiver doesn't have one.
2276 } else if (ov || !(i in r)) {
2279 // if merge is specified and the receiver is an array,
2280 // append the array item
2281 // } else if (arr) {
2296 * Returns a wrapper for a function which caches the
2297 * return value of that function, keyed off of the combined
2300 * @param source {function} the function to memoize.
2301 * @param cache an optional cache seed.
2302 * @param refetch if supplied, this value is tested against the cached
2303 * value. If the values are equal, the wrapped function is executed again.
2304 * @return {Function} the wrapped function.
2306 Y.cached = function(source, cache, refetch) {
2307 cache = cache || {};
2309 return function(arg1) {
2311 var k = (arguments.length > 1) ?
2312 Array.prototype.join.call(arguments, CACHED_DELIMITER) : arg1;
2314 if (!(k in cache) || (refetch && cache[k] == refetch)) {
2315 cache[k] = source.apply(source, arguments);
2324 * The YUI module contains the components required for building the YUI
2325 * seed file. This includes the script loading mechanism, a simple queue,
2326 * and the core utilities for the library.
2328 * @submodule yui-base
2332 * Adds the following Object utilities to the YUI instance
2337 * Y.Object(o) returns a new object based upon the supplied object.
2340 * @param o the supplier object.
2341 * @return {Object} the new object.
2343 var F = function() {},
2345 // O = Object.create || function(o) {
2355 owns = function(o, k) {
2356 return o && o.hasOwnProperty && o.hasOwnProperty(k);
2357 // return Object.prototype.hasOwnProperty.call(o, k);
2363 * Extracts the keys, values, or size from an object
2366 * @param o the object.
2367 * @param what what to extract (0: keys, 1: values, 2: size).
2368 * @return {boolean|Array} the extracted info.
2372 _extract = function(o, what) {
2373 var count = (what === 2), out = (count) ? 0 : [], i;
2380 out.push((what) ? o[i] : i);
2391 * Returns an array containing the object's keys
2394 * @param o an object.
2395 * @return {string[]} the keys.
2397 // O.keys = Object.keys || function(o) {
2398 // return _extract(o);
2401 O.keys = function(o) {
2406 * Returns an array containing the object's values
2409 * @param o an object.
2410 * @return {Array} the values.
2412 // O.values = Object.values || function(o) {
2413 // return _extract(o, 1);
2416 O.values = function(o) {
2417 return _extract(o, 1);
2421 * Returns the size of an object
2424 * @param o an object.
2425 * @return {int} the size.
2427 O.size = Object.size || function(o) {
2428 return _extract(o, 2);
2432 * Returns true if the object contains a given key
2435 * @param o an object.
2436 * @param k the key to query.
2437 * @return {boolean} true if the object contains the key.
2441 * Returns true if the object contains a given value
2444 * @param o an object.
2445 * @param v the value to query.
2446 * @return {boolean} true if the object contains the value.
2448 O.hasValue = function(o, v) {
2449 return (Y.Array.indexOf(O.values(o), v) > -1);
2453 * Determines whether or not the property was added
2454 * to the object instance. Returns false if the property is not present
2455 * in the object, or was inherited from the prototype.
2459 * @param o {any} The object being testing.
2460 * @param p {string} the property to look for.
2461 * @return {boolean} true if the object has the property on the instance.
2466 * Executes a function on each item. The function
2467 * receives the value, the key, and the object
2468 * as parameters (in that order).
2471 * @param o the object to iterate.
2472 * @param f {Function} the function to execute on each item. The function
2473 * receives three arguments: the value, the the key, the full object.
2474 * @param c the execution context.
2475 * @param proto {boolean} include proto.
2476 * @return {YUI} the YUI instance.
2478 O.each = function(o, f, c, proto) {
2482 if (proto || owns(o, i)) {
2483 f.call(s, o[i], i, o);
2490 * Executes a function on each item, but halts if the
2491 * function returns true. The function
2492 * receives the value, the key, and the object
2493 * as paramters (in that order).
2496 * @param o the object to iterate.
2497 * @param f {Function} the function to execute on each item. The function
2498 * receives three arguments: the value, the the key, the full object.
2499 * @param c the execution context.
2500 * @param proto {boolean} include proto.
2501 * @return {boolean} true if any execution of the function returns true,
2504 O.some = function(o, f, c, proto) {
2508 if (proto || owns(o, i)) {
2509 if (f.call(s, o[i], i, o)) {
2518 * Retrieves the sub value at the provided path,
2519 * from the value object provided.
2523 * @param o The object from which to extract the property value.
2524 * @param path {Array} A path array, specifying the object traversal path
2525 * from which to obtain the sub value.
2526 * @return {Any} The value stored in the path, undefined if not found,
2527 * undefined if the source is not an object. Returns the source object
2528 * if an empty path is provided.
2530 O.getValue = function(o, path) {
2531 if (!Y.Lang.isObject(o)) {
2539 for (i = 0; o !== UNDEF && i < l; i++) {
2547 * Sets the sub-attribute value at the provided path on the
2548 * value object. Returns the modified value object, or
2549 * undefined if the path is invalid.
2553 * @param o The object on which to set the sub value.
2554 * @param path {Array} A path array, specifying the object traversal path
2555 * at which to set the sub value.
2556 * @param val {Any} The new value for the sub-attribute.
2557 * @return {Object} The modified object, with the new sub value set, or
2558 * undefined, if the path was invalid.
2560 O.setValue = function(o, path, val) {
2563 leafIdx = p.length - 1,
2567 for (i = 0; ref !== UNDEF && i < leafIdx; i++) {
2571 if (ref !== UNDEF) {
2582 * Returns true if the object has no properties of its own
2585 * @return {boolean} true if the object is empty.
2588 O.isEmpty = function(o) {
2597 * The YUI module contains the components required for building the YUI seed
2598 * file. This includes the script loading mechanism, a simple queue, and the
2599 * core utilities for the library.
2601 * @submodule yui-base
2605 * YUI user agent detection.
2606 * Do not fork for a browser if it can be avoided. Use feature detection when
2607 * you can. Use the user agent as a last resort. UA stores a version
2608 * number for the browser engine, 0 otherwise. This value may or may not map
2609 * to the version number of the browser using the engine. The value is
2610 * presented as a float so that it can easily be used for boolean evaluation
2611 * as well as for looking for a particular range of versions. Because of this,
2612 * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
2618 * Static method for parsing the UA string. Defaults to assigning it's value to Y.UA
2620 * @method Env.parseUA
2621 * @param {String} subUA Parse this UA string instead of navigator.userAgent
2622 * @returns {Object} The Y.UA object
2624 YUI.Env.parseUA = function(subUA) {
2626 var numberify = function(s) {
2628 return parseFloat(s.replace(/\./g, function() {
2629 return (c++ == 1) ? '' : '.';
2635 nav = win && win.navigator,
2640 * Internet Explorer version number or 0. Example: 6
2648 * Opera version number or 0. Example: 9.2
2656 * Gecko engine revision number. Will evaluate to 1 if Gecko
2657 * is detected but the revision could not be found. Other browsers
2658 * will be 0. Example: 1.8
2660 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
2661 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8
2662 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81
2663 * Firefox 3.0 <-- 1.9
2664 * Firefox 3.5 <-- 1.91
2673 * AppleWebKit version. KHTML browsers that are not WebKit browsers
2674 * will evaluate to 1, other browsers 0. Example: 418.9
2676 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
2677 * latest available for Mac OSX 10.3.
2678 * Safari 2.0.2: 416 <-- hasOwnProperty introduced
2679 * Safari 2.0.4: 418 <-- preventDefault fixed
2680 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
2681 * different versions of webkit
2682 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
2683 * updated, but not updated
2684 * to the latest patch.
2685 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native
2686 * SVG and many major issues fixed).
2687 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic
2688 * update from 2.x via the 10.4.11 OS patch.
2689 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
2690 * yahoo.com user agent hack removed.
2692 * http://en.wikipedia.org/wiki/Safari_version_history
2700 * Chrome will be detected as webkit, but this property will also
2701 * be populated with the Chrome version number
2709 * The mobile property will be set to a string containing any relevant
2710 * user agent information when a modern mobile browser is detected.
2711 * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
2712 * devices with the WebKit-based browser, and Opera Mini.
2720 * Adobe AIR version number or 0. Only populated if webkit is detected.
2727 * Detects Apple iPad's OS version
2734 * Detects Apple iPhone's OS version
2741 * Detects Apples iPod's OS version
2748 * General truthy check for iPad, iPhone or iPod
2755 * Detects Googles Android OS version
2762 * Detects Palms WebOS version
2770 * Google Caja version number or 0.
2774 caja: nav && nav.cajaVersion,
2777 * Set to true if the page appears to be in SSL
2785 * The operating system. Currently only detecting windows or macintosh
2794 ua = subUA || nav && nav.userAgent,
2796 loc = win && win.location,
2798 href = loc && loc.href,
2802 o.secure = href && (href.toLowerCase().indexOf('https') === 0);
2806 if ((/windows|win32/i).test(ua)) {
2808 } else if ((/macintosh/i).test(ua)) {
2810 } else if ((/rhino/i).test(ua)) {
2814 // Modern KHTML browsers should qualify as Safari X-Grade
2815 if ((/KHTML/).test(ua)) {
2818 // Modern WebKit browsers are at least X-Grade
2819 m = ua.match(/AppleWebKit\/([^\s]*)/);
2821 o.webkit = numberify(m[1]);
2823 // Mobile browser check
2824 if (/ Mobile\//.test(ua)) {
2825 o.mobile = 'Apple'; // iPhone or iPod Touch
2827 m = ua.match(/OS ([^\s]*)/);
2829 m = numberify(m[1].replace('_', '.'));
2832 o.ipad = o.ipod = o.iphone = 0;
2834 m = ua.match(/iPad|iPod|iPhone/);
2836 o[m[0].toLowerCase()] = o.ios;
2839 m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
2841 // Nokia N-series, Android, webOS, ex: NokiaN95
2844 if (/webOS/.test(ua)) {
2846 m = ua.match(/webOS\/([^\s]*);/);
2848 o.webos = numberify(m[1]);
2851 if (/ Android/.test(ua)) {
2852 o.mobile = 'Android';
2853 m = ua.match(/Android ([^\s]*);/);
2855 o.android = numberify(m[1]);
2861 m = ua.match(/Chrome\/([^\s]*)/);
2863 o.chrome = numberify(m[1]); // Chrome
2865 m = ua.match(/AdobeAIR\/([^\s]*)/);
2867 o.air = m[0]; // Adobe AIR 1.0 or better
2872 if (!o.webkit) { // not webkit
2873 // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
2874 m = ua.match(/Opera[\s\/]([^\s]*)/);
2876 o.opera = numberify(m[1]);
2877 m = ua.match(/Opera Mini[^;]*/);
2879 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
2881 } else { // not opera or webkit
2882 m = ua.match(/MSIE\s([^;]*)/);
2884 o.ie = numberify(m[1]);
2885 } else { // not opera, webkit, or ie
2886 m = ua.match(/Gecko\/([^\s]*)/);
2888 o.gecko = 1; // Gecko detected, look for revision
2889 m = ua.match(/rv:([^\s\)]*)/);
2891 o.gecko = numberify(m[1]);
2905 Y.UA = YUI.Env.UA || YUI.Env.parseUA();
2909 YUI.add('get', function(Y) {
2913 * Provides a mechanism to fetch remote resources and
2914 * insert them into a document.
2921 TYPE_JS = 'text/javascript',
2922 TYPE_CSS = 'text/css',
2923 STYLESHEET = 'stylesheet';
2926 * Fetches and inserts one or more script or link nodes into the document
2930 Y.Get = function() {
2933 * hash of queues to manage multiple requests
2937 var _get, _purge, _track,
2942 * queue index used to generate transaction ids
2950 * interal property used to prevent multiple simultaneous purge
2960 * Generates an HTML element, this is not appended to a document
2962 * @param {string} type the type of element.
2963 * @param {string} attr the attributes.
2964 * @param {Window} win optional window to create the element in.
2965 * @return {HTMLElement} the generated node.
2968 _node = function(type, attr, win) {
2969 var w = win || Y.config.win,
2971 n = d.createElement(type),
2975 if (attr[i] && attr.hasOwnProperty(i)) {
2976 n.setAttribute(i, attr[i]);
2984 * Generates a link node
2986 * @param {string} url the url for the css file.
2987 * @param {Window} win optional window to create the node in.
2988 * @param {object} attributes optional attributes collection to apply to the
2990 * @return {HTMLElement} the generated node.
2993 _linkNode = function(url, win, attributes) {
3001 Y.mix(o, attributes);
3003 return _node('link', o, win);
3007 * Generates a script node
3008 * @method _scriptNode
3009 * @param {string} url the url for the script file.
3010 * @param {Window} win optional window to create the node in.
3011 * @param {object} attributes optional attributes collection to apply to the
3013 * @return {HTMLElement} the generated node.
3016 _scriptNode = function(url, win, attributes) {
3023 Y.mix(o, attributes);
3028 return _node('script', o, win);
3032 * Returns the data payload for callback functions.
3033 * @method _returnData
3034 * @param {object} q the queue.
3035 * @param {string} msg the result message.
3036 * @param {string} result the status message from the request.
3037 * @return {object} the state data from the request.
3040 _returnData = function(q, msg, result) {
3055 * The transaction is finished
3057 * @param {string} id the id of the request.
3058 * @param {string} msg the result message.
3059 * @param {string} result the status message from the request.
3062 _end = function(id, msg, result) {
3063 var q = queues[id], sc;
3065 sc = q.context || q;
3066 q.onEnd.call(sc, _returnData(q, msg, result));
3071 * The request failed, execute fail handler with whatever
3072 * was accomplished. There isn't a failure case at the
3073 * moment unless you count aborted transactions
3075 * @param {string} id the id of the request
3078 _fail = function(id, msg) {
3080 var q = queues[id], sc;
3082 // q.timer.cancel();
3083 clearTimeout(q.timer);
3086 // execute failure callback
3088 sc = q.context || q;
3089 q.onFailure.call(sc, _returnData(q, msg));
3092 _end(id, msg, 'failure');
3096 * The request is complete, so executing the requester's callback
3098 * @param {string} id the id of the request.
3101 _finish = function(id) {
3102 var q = queues[id], msg, sc;
3104 // q.timer.cancel();
3105 clearTimeout(q.timer);
3110 msg = 'transaction ' + id + ' was aborted';
3115 // execute success callback
3117 sc = q.context || q;
3118 q.onSuccess.call(sc, _returnData(q));
3121 _end(id, msg, 'OK');
3127 * @param {string} id the id of the request.
3130 _timeout = function(id) {
3131 var q = queues[id], sc;
3133 sc = q.context || q;
3134 q.onTimeout.call(sc, _returnData(q));
3137 _end(id, 'timeout', 'timeout');
3142 * Loads the next item for a given request
3144 * @param {string} id the id of the request.
3145 * @param {string} loaded the url that was just loaded, if any.
3146 * @return {string} the result.
3149 _next = function(id, loaded) {
3150 var q = queues[id], msg, w, d, h, n, url, s,
3154 // q.timer.cancel();
3155 clearTimeout(q.timer);
3159 msg = 'transaction ' + id + ' was aborted';
3170 // This is the first pass: make sure the url is an array
3171 q.url = (L.isString(q.url)) ? [q.url] : q.url;
3173 q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
3179 h = d.getElementsByTagName('head')[0];
3181 if (q.url.length === 0) {
3188 // if the url is undefined, this is probably a trailing comma
3197 // q.timer = L.later(q.timeout, q, _timeout, id);
3198 q.timer = setTimeout(function() {
3203 if (q.type === 'script') {
3204 n = _scriptNode(url, w, q.attributes);
3206 n = _linkNode(url, w, q.attributes);
3209 // track this node's load progress
3210 _track(q.type, n, id, url, w, q.url.length);
3212 // add the node to the queue so we can return it to the user supplied
3216 // add it to the head or insert it before 'insertBefore'. Work around
3217 // IE bug if there is a base tag.
3218 insertBefore = q.insertBefore ||
3219 d.getElementsByTagName('base')[0];
3222 s = _get(insertBefore, id);
3224 s.parentNode.insertBefore(n, s);
3231 // FireFox does not support the onload event for link nodes, so
3232 // there is no way to make the css requests synchronous. This means
3233 // that the css rules in multiple files could be applied out of order
3234 // in this browser if a later request returns before an earlier one.
3236 if ((ua.webkit || ua.gecko) && q.type === 'css') {
3242 * Removes processed queues and corresponding nodes
3243 * @method _autoPurge
3246 _autoPurge = function() {
3255 if (queues.hasOwnProperty(i)) {
3257 if (q.autopurge && q.finished) {
3268 * Saves the state for the request and begins loading
3269 * the requested urls
3271 * @param {string} type the type of node to insert.
3272 * @param {string} url the url to load.
3273 * @param {object} opts the hash of options for this request.
3274 * @return {object} transaction object.
3277 _queue = function(type, url, opts) {
3280 var id = 'q' + (qidx++), q,
3281 thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
3283 if (qidx % thresh === 0) {
3287 queues[id] = Y.merge(opts, {
3296 q.win = q.win || Y.config.win;
3297 q.context = q.context || q;
3298 q.autopurge = ('autopurge' in q) ? q.autopurge :
3299 (type === 'script') ? true : false;
3301 q.attributes = q.attributes || {};
3302 q.attributes.charset = opts.charset || q.attributes.charset || 'utf-8';
3312 * Detects when a node has been loaded. In the case of
3313 * script nodes, this does not guarantee that contained
3314 * script is ready to use.
3316 * @param {string} type the type of node to track.
3317 * @param {HTMLElement} n the node to track.
3318 * @param {string} id the id of the request.
3319 * @param {string} url the url that is being loaded.
3320 * @param {Window} win the targeted window.
3321 * @param {int} qlength the number of remaining items in the queue,
3322 * including this one.
3323 * @param {Function} trackfn function to execute when finished
3324 * the default is _next.
3327 _track = function(type, n, id, url, win, qlength, trackfn) {
3328 var f = trackfn || _next;
3330 // IE supports the readystatechange event for script and css nodes
3331 // Opera only for script nodes. Opera support onload for script
3332 // nodes, but this doesn't fire when there is a load failure.
3333 // The onreadystatechange appears to be a better way to respond
3334 // to both success and failure.
3336 n.onreadystatechange = function() {
3337 var rs = this.readyState;
3338 if ('loaded' === rs || 'complete' === rs) {
3339 n.onreadystatechange = null;
3344 // webkit prior to 3.x is no longer supported
3345 } else if (ua.webkit) {
3346 if (type === 'script') {
3347 // Safari 3.x supports the load event for script nodes (DOM2)
3348 n.addEventListener('load', function() {
3353 // FireFox and Opera support onload (but not DOM2 in FF) handlers for
3354 // script nodes. Opera, but not FF, supports the onload event for link
3357 n.onload = function() {
3361 n.onerror = function(e) {
3362 _fail(id, e + ': ' + url);
3367 _get = function(nId, tId) {
3368 var q = queues[tId],
3369 n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
3371 _fail(tId, 'target node not found: ' + nId);
3378 * Removes the nodes for the specified queue
3380 * @param {string} tId the transaction id.
3383 _purge = function(tId) {
3384 var n, l, d, h, s, i, node, attr, insertBefore,
3391 h = d.getElementsByTagName('head')[0];
3393 insertBefore = q.insertBefore ||
3394 d.getElementsByTagName('base')[0];
3397 s = _get(insertBefore, tId);
3403 for (i = 0; i < l; i = i + 1) {
3405 if (node.clearAttributes) {
3406 node.clearAttributes();
3408 for (attr in node) {
3409 if (node.hasOwnProperty(attr)) {
3415 h.removeChild(node);
3424 * The number of request required before an automatic purge.
3425 * Can be configured via the 'purgethreshold' config
3426 * property PURGE_THRESH
3435 * Called by the the helper for detecting script load in Safari
3438 * @param {string} id the transaction id.
3441 _finalize: function(id) {
3442 setTimeout(function() {
3448 * Abort a transaction
3451 * @param {string|object} o Either the tId or the object returned from
3452 * script() or css().
3454 abort: function(o) {
3455 var id = (L.isString(o)) ? o : o.tId,
3463 * Fetches and inserts one or more script nodes into the head
3464 * of the current document or the document in a specified window.
3468 * @param {string|string[]} url the url or urls to the script(s).
3469 * @param {object} opts Options:
3471 * <dt>onSuccess</dt>
3473 * callback to execute when the script(s) are finished loading
3474 * The callback receives an object back with the following
3478 * <dd>the window the script(s) were inserted into</dd>
3480 * <dd>the data object passed in when the request was made</dd>
3482 * <dd>An array containing references to the nodes that were
3485 * <dd>A function that, when executed, will remove the nodes
3486 * that were inserted</dd>
3490 * <dt>onTimeout</dt>
3492 * callback to execute when a timeout occurs.
3493 * The callback receives an object back with the following
3497 * <dd>the window the script(s) were inserted into</dd>
3499 * <dd>the data object passed in when the request was made</dd>
3501 * <dd>An array containing references to the nodes that were
3504 * <dd>A function that, when executed, will remove the nodes
3505 * that were inserted</dd>
3510 * <dd>a function that executes when the transaction finishes,
3511 * regardless of the exit path</dd>
3512 * <dt>onFailure</dt>
3514 * callback to execute when the script load operation fails
3515 * The callback receives an object back with the following
3519 * <dd>the window the script(s) were inserted into</dd>
3521 * <dd>the data object passed in when the request was made</dd>
3523 * <dd>An array containing references to the nodes that were
3524 * inserted successfully</dd>
3526 * <dd>A function that, when executed, will remove any nodes
3527 * that were inserted</dd>
3532 * <dd>the execution context for the callbacks</dd>
3534 * <dd>a window other than the one the utility occupies</dd>
3535 * <dt>autopurge</dt>
3537 * setting to true will let the utilities cleanup routine purge
3538 * the script once loaded
3540 * <dt>purgethreshold</dt>
3542 * The number of transaction before autopurge should be initiated
3546 * data that is supplied to the callback when the script(s) are
3549 * <dt>insertBefore</dt>
3550 * <dd>node or node id that will become the new node's nextSibling.
3551 * If this is not specified, nodes will be inserted before a base
3552 * tag should it exist. Otherwise, the nodes will be appended to the
3553 * end of the document head.</dd>
3556 * <dd>Node charset, default utf-8 (deprecated, use the attributes
3558 * <dt>attributes</dt>
3559 * <dd>An object literal containing additional attributes to add to
3560 * the link tags</dd>
3562 * <dd>Number of milliseconds to wait before aborting and firing
3563 * the timeout event</dd>
3565 * Y.Get.script(
3566 * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
3567 * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"],
3569 * onSuccess: function(o) {
3570 * this.log("won't cause error because Y is the context");
3571 * // immediately
3573 * onFailure: function(o) {
3575 * onTimeout: function(o) {
3577 * data: "foo",
3578 * timeout: 10000, // 10 second timeout
3579 * context: Y, // make the YUI instance
3580 * // win: otherframe // target another window/frame
3581 * autopurge: true // allow the utility to choose when to
3582 * // remove the nodes
3583 * purgetheshold: 1 // purge previous transaction before
3584 * // next transaction
3587 * @return {tId: string} an object containing info about the
3590 script: function(url, opts) {
3591 return _queue('script', url, opts);
3595 * Fetches and inserts one or more css link nodes into the
3596 * head of the current document or the document in a specified
3600 * @param {string} url the url or urls to the css file(s).
3601 * @param {object} opts Options:
3603 * <dt>onSuccess</dt>
3605 * callback to execute when the css file(s) are finished loading
3606 * The callback receives an object back with the following
3609 * <dd>the window the link nodes(s) were inserted into</dd>
3611 * <dd>the data object passed in when the request was made</dd>
3613 * <dd>An array containing references to the nodes that were
3616 * <dd>A function that, when executed, will remove the nodes
3617 * that were inserted</dd>
3622 * <dd>the execution context for the callbacks</dd>
3624 * <dd>a window other than the one the utility occupies</dd>
3627 * data that is supplied to the callbacks when the nodes(s) are
3630 * <dt>insertBefore</dt>
3631 * <dd>node or node id that will become the new node's nextSibling</dd>
3633 * <dd>Node charset, default utf-8 (deprecated, use the attributes
3635 * <dt>attributes</dt>
3636 * <dd>An object literal containing additional attributes to add to
3637 * the link tags</dd>
3640 * Y.Get.css("http://localhost/css/menu.css");
3644 * ["http://localhost/css/menu.css",
3645 * insertBefore: 'custom-styles' // nodes will be inserted
3646 * // before the specified node
3649 * @return {tId: string} an object containing info about the
3652 css: function(url, opts) {
3653 return _queue('css', url, opts);
3660 }, '3.3.0' ,{requires:['yui-base']});
3661 YUI.add('features', function(Y) {
3663 var feature_tests = {};
3665 Y.mix(Y.namespace('Features'), {
3667 tests: feature_tests,
3669 add: function(cat, name, o) {
3670 feature_tests[cat] = feature_tests[cat] || {};
3671 feature_tests[cat][name] = o;
3674 all: function(cat, args) {
3675 var cat_o = feature_tests[cat],
3679 Y.Object.each(cat_o, function(v, k) {
3680 // results[k] = Y.Features.test(cat, k, args);
3682 (Y.Features.test(cat, k, args) ? 1 : 0) + ';';
3689 test: function(cat, name, args) {
3691 var result, ua, test,
3692 cat_o = feature_tests[cat],
3693 feature = cat_o && cat_o[name];
3698 result = feature.result;
3700 if (Y.Lang.isUndefined(result)) {
3704 result = (Y.UA[ua]);
3707 test = feature.test;
3708 if (test && ((!ua) || result)) {
3709 result = test.apply(Y, args);
3712 feature.result = result;
3720 // Y.Features.add("load", "1", {});
3721 // Y.Features.test("load", "1");
3722 // caps=1:1;2:0;3:1;
3724 /* This file is auto-generated by src/loader/meta_join.py */
3725 var add = Y.Features.add;
3726 // autocomplete-list-keys-sniff.js
3728 "test": function (Y) {
3729 // Only add keyboard support to autocomplete-list if this doesn't appear to
3730 // be an iOS or Android-based mobile device.
3732 // There's currently no feasible way to actually detect whether a device has
3733 // a hardware keyboard, so this sniff will have to do. It can easily be
3734 // overridden by manually loading the autocomplete-list-keys module.
3736 // Worth noting: even though iOS supports bluetooth keyboards, Mobile Safari
3737 // doesn't fire the keyboard events used by AutoCompleteList, so there's
3738 // no point loading the -keys module even when a bluetooth keyboard may be
3740 return !(Y.UA.ios || Y.UA.android);
3742 "trigger": "autocomplete-list"
3746 "test": function (Y) {
3748 var testFeature = Y.Features.test,
3749 addFeature = Y.Features.add,
3750 WINDOW = Y.config.win,
3751 DOCUMENT = Y.config.doc,
3752 DOCUMENT_ELEMENT = 'documentElement',
3755 addFeature('style', 'computedStyle', {
3757 return WINDOW && 'getComputedStyle' in WINDOW;
3761 addFeature('style', 'opacity', {
3763 return DOCUMENT && 'opacity' in DOCUMENT[DOCUMENT_ELEMENT].style;
3767 ret = (!testFeature('style', 'opacity') &&
3768 !testFeature('style', 'computedStyle'));
3772 "trigger": "dom-style"
3776 "trigger": "widget-base",
3781 "test": function(Y) {
3782 var imp = Y.config.doc && Y.config.doc.implementation;
3783 return (imp && (!imp.hasFeature('Events', '2.0')));
3785 "trigger": "node-base"
3787 // dd-gestures-test.js
3789 "test": function(Y) {
3790 return (Y.config.win && ('ontouchstart' in Y.config.win && !Y.UA.chrome));
3792 "trigger": "dd-drag"
3794 // history-hash-ie-test.js
3796 "test": function (Y) {
3797 var docMode = Y.config.doc.documentMode;
3799 return Y.UA.ie && (!('onhashchange' in Y.config.win) ||
3800 !docMode || docMode < 8);
3802 "trigger": "history-hash"
3806 }, '3.3.0' ,{requires:['yui-base']});
3807 YUI.add('rls', function(Y) {
3810 * Implentation for building the remote loader service url.
3812 * @param {Array} what the requested modules.
3814 * @return {string} the url for the remote loader service call.
3816 Y._rls = function(what) {
3818 var config = Y.config,
3820 // the configuration
3821 rls = config.rls || {
3822 m: 1, // required in the template
3825 env: 1, // required in the template
3827 '2in3v': config['2in3'],
3829 filt: config.filter,
3830 filts: config.filters,
3831 tests: 1 // required in the template
3834 // The rls base path
3835 rls_base = config.rls_base || 'load?',
3838 rls_tmpl = config.rls_tmpl || function() {
3840 for (param in rls) {
3841 if (param in rls && rls[param]) {
3842 s += param + '={' + param + '}&';
3845 // console.log('rls_tmpl: ' + s);
3851 // update the request
3853 rls.env = Y.Object.keys(YUI.Env.mods);
3854 rls.tests = Y.Features.all('load', [Y]);
3856 url = Y.Lang.sub(rls_base + rls_tmpl, rls);
3859 config.rls_tmpl = rls_tmpl;
3861 // console.log(url);
3867 }, '3.3.0' ,{requires:['get','features']});
3868 YUI.add('intl-base', function(Y) {
3871 * The Intl utility provides a central location for managing sets of
3872 * localized resources (strings and formatting patterns).
3879 var SPLIT_REGEX = /[, ]/;
3881 Y.mix(Y.namespace('Intl'), {
3884 * Returns the language among those available that
3885 * best matches the preferred language list, using the Lookup
3886 * algorithm of BCP 47.
3887 * If none of the available languages meets the user's preferences,
3888 * then "" is returned.
3889 * Extended language ranges are not supported.
3891 * @method lookupBestLang
3892 * @param {String[] | String} preferredLanguages The list of preferred
3893 * languages in descending preference order, represented as BCP 47
3894 * language tags. A string array or a comma-separated list.
3895 * @param {String[]} availableLanguages The list of languages
3896 * that the application supports, represented as BCP 47 language
3899 * @return {String} The available language that best matches the
3900 * preferred language list, or "".
3903 lookupBestLang: function(preferredLanguages, availableLanguages) {
3905 var i, language, result, index;
3907 // check whether the list of available languages contains language;
3909 function scan(language) {
3911 for (i = 0; i < availableLanguages.length; i += 1) {
3912 if (language.toLowerCase() ===
3913 availableLanguages[i].toLowerCase()) {
3914 return availableLanguages[i];
3919 if (Y.Lang.isString(preferredLanguages)) {
3920 preferredLanguages = preferredLanguages.split(SPLIT_REGEX);
3923 for (i = 0; i < preferredLanguages.length; i += 1) {
3924 language = preferredLanguages[i];
3925 if (!language || language === '*') {
3928 // check the fallback sequence for one language
3929 while (language.length > 0) {
3930 result = scan(language);
3934 index = language.lastIndexOf('-');
3936 language = language.substring(0, index);
3937 // one-character subtags get cut along with the
3939 if (index >= 2 && language.charAt(index - 2) === '-') {
3940 language = language.substring(0, index - 2);
3943 // nothing available for this language
3955 }, '3.3.0' ,{requires:['yui-base']});
3956 YUI.add('yui-log', function(Y) {
3959 * Provides console log capability and exposes a custom event for
3960 * console implementations.
3962 * @submodule yui-log
3966 LOGEVENT = 'yui:log',
3967 UNDEFINED = 'undefined',
3968 LEVELS = { debug: 1,
3974 * If the 'debug' config is true, a 'yui:log' event will be
3975 * dispatched, which the Console widget and anything else
3976 * can consume. If the 'useBrowserConsole' config is true, it will
3977 * write to the browser console if available. YUI-specific log
3978 * messages will only be present in the -debug versions of the
3979 * JS files. The build system is supposed to remove log statements
3980 * from the raw and minified versions of the files.
3984 * @param {String} msg The message to log.
3985 * @param {String} cat The log category for the message. Default
3986 * categories are "info", "warn", "error", time".
3987 * Custom categories can be used as well. (opt).
3988 * @param {String} src The source of the the message (opt).
3989 * @param {boolean} silent If true, the log event won't fire.
3990 * @return {YUI} YUI instance.
3992 INSTANCE.log = function(msg, cat, src, silent) {
3993 var bail, excl, incl, m, f,
3996 publisher = (Y.fire) ? Y : YUI.Env.globalEvents;
3997 // suppress log message if the config is off or the event stack
3998 // or the event call stack contains a consumer of the yui:log event
4000 // apply source filters
4002 excl = c.logExclude;
4003 incl = c.logInclude;
4004 if (incl && !(src in incl)) {
4006 } else if (excl && (src in excl)) {
4011 if (c.useBrowserConsole) {
4012 m = (src) ? src + ': ' + msg : msg;
4013 if (Y.Lang.isFunction(c.logFn)) {
4014 c.logFn.call(Y, msg, cat, src);
4015 } else if (typeof console != UNDEFINED && console.log) {
4016 f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
4018 } else if (typeof opera != UNDEFINED) {
4023 if (publisher && !silent) {
4025 if (publisher == Y && (!publisher.getEvent(LOGEVENT))) {
4026 publisher.publish(LOGEVENT, {
4031 publisher.fire(LOGEVENT, {
4044 * Write a system message. This message will be preserved in the
4045 * minified and raw versions of the YUI files, unlike log statements.
4048 * @param {String} msg The message to log.
4049 * @param {String} cat The log category for the message. Default
4050 * categories are "info", "warn", "error", time".
4051 * Custom categories can be used as well. (opt).
4052 * @param {String} src The source of the the message (opt).
4053 * @param {boolean} silent If true, the log event won't fire.
4054 * @return {YUI} YUI instance.
4056 INSTANCE.message = function() {
4057 return INSTANCE.log.apply(INSTANCE, arguments);
4061 }, '3.3.0' ,{requires:['yui-base']});
4062 YUI.add('yui-later', function(Y) {
4065 * Provides a setTimeout/setInterval wrapper
4067 * @submodule yui-later
4071 * Executes the supplied function in the context of the supplied
4072 * object 'when' milliseconds later. Executes the function a
4073 * single time unless periodic is set to true.
4076 * @param when {int} the number of milliseconds to wait until the fn
4078 * @param o the context object.
4079 * @param fn {Function|String} the function to execute or the name of
4080 * the method in the 'o' object to execute.
4081 * @param data [Array] data that is provided to the function. This
4082 * accepts either a single item or an array. If an array is provided,
4083 * the function is executed with one parameter for each array item.
4084 * If you need to pass a single array parameter, it needs to be wrapped
4085 * in an array [myarray].
4086 * @param periodic {boolean} if true, executes continuously at supplied
4087 * interval until canceled.
4088 * @return {object} a timer object. Call the cancel() method on this
4089 * object to stop the timer.
4091 Y.later = function(when, o, fn, data, periodic) {
4096 if (o && Y.Lang.isString(fn)) {
4100 f = !Y.Lang.isUndefined(data) ? function() {
4101 m.apply(o, Y.Array(data));
4106 id = (periodic) ? setInterval(f, when) : setTimeout(f, when);
4111 cancel: function() {
4112 if (this.interval) {
4121 Y.Lang.later = Y.later;
4125 }, '3.3.0' ,{requires:['yui-base']});
4126 YUI.add('yui-throttle', function(Y) {
4129 * Provides a throttle/limiter for function calls
4131 * @submodule yui-throttle
4134 /*! Based on work by Simon Willison: http://gist.github.com/292562 */
4136 * Throttles a call to a method based on the time between calls.
4139 * @param fn {function} The function call to throttle.
4140 * @param ms {int} The number of milliseconds to throttle the method call.
4141 * Can set globally with Y.config.throttleTime or by call. Passing a -1 will
4142 * disable the throttle. Defaults to 150.
4143 * @return {function} Returns a wrapped function that calls fn throttled.
4146 Y.throttle = function(fn, ms) {
4147 ms = (ms) ? ms : (Y.config.throttleTime || 150);
4150 return (function() {
4151 fn.apply(null, arguments);
4155 var last = Y.Lang.now();
4157 return (function() {
4158 var now = Y.Lang.now();
4159 if (now - last > ms) {
4161 fn.apply(null, arguments);
4167 }, '3.3.0' ,{requires:['yui-base']});
4170 YUI.add('yui', function(Y){}, '3.3.0' ,{use:['yui-base','get','features','rls','intl-base','yui-log','yui-later','yui-throttle']});
4172 YUI.add('oop', function(Y) {
4175 * Supplies object inheritance and manipulation utilities. This adds
4176 * additional functionaity to what is provided in yui-base, and the
4177 * methods are applied directly to the YUI instance. This module
4178 * is required for most YUI components.
4183 * The following methods are added to the YUI instance
4189 OP = Object.prototype,
4190 CLONE_MARKER = '_~yuim~_',
4194 dispatch = function(o, f, c, proto, action) {
4195 if (o && o[action] && o !== Y) {
4196 return o[action].call(o, f, c);
4198 switch (A.test(o)) {
4200 return A[action](o, f, c);
4202 return A[action](Y.Array(o, 0, true), f, c);
4204 return Y.Object[action](o, f, c, proto);
4211 * Applies prototype properties from the supplier to the receiver.
4212 * The receiver can be a constructor or an instance.
4214 * @param {function} r the object to receive the augmentation.
4215 * @param {function} s the object that supplies the properties to augment.
4216 * @param {boolean} ov if true, properties already on the receiver
4217 * will be overwritten if found on the supplier.
4218 * @param {string[]} wl a whitelist. If supplied, only properties in
4219 * this list will be applied to the receiver.
4220 * @param {Array | Any} args arg or arguments to apply to the supplier
4221 * constructor when initializing.
4222 * @return {object} the augmented object.
4224 * @todo constructor optional?
4225 * @todo understanding what an instance is augmented with
4226 * @todo best practices for overriding sequestered methods.
4228 Y.augment = function(r, s, ov, wl, args) {
4229 var sProto = s.prototype,
4232 a = (args) ? Y.Array(args) : [],
4233 rProto = r.prototype,
4234 target = rProto || r,
4235 applyConstructor = false,
4236 sequestered, replacements;
4238 // working on a class, so apply constructor infrastructure
4239 if (rProto && construct) {
4244 // sequester all of the functions in the supplier and replace with
4245 // one that will restore all of them.
4246 Y.Object.each(sProto, function(v, k) {
4247 replacements[k] = function() {
4249 // overwrite the prototype with all of the sequestered functions,
4250 // but only if it hasn't been overridden
4251 for (var i in sequestered) {
4252 if (sequestered.hasOwnProperty(i) &&
4253 (this[i] === replacements[i])) {
4254 this[i] = sequestered[i];
4258 // apply the constructor
4259 construct.apply(this, a);
4261 // apply the original sequestered function
4262 return sequestered[k].apply(this, arguments);
4265 if ((!wl || (k in wl)) && (ov || !(k in this))) {
4266 if (L.isFunction(v)) {
4267 // sequester the function
4270 // replace the sequestered function with a function that will
4271 // restore all sequestered functions and exectue the constructor.
4272 this[k] = replacements[k];
4280 // augmenting an instance, so apply the constructor immediately
4282 applyConstructor = true;
4285 Y.mix(target, newProto || sProto, ov, wl);
4287 if (applyConstructor) {
4295 * Applies object properties from the supplier to the receiver. If
4296 * the target has the property, and the property is an object, the target
4297 * object will be augmented with the supplier's value. If the property
4298 * is an array, the suppliers value will be appended to the target.
4300 * @param {function} r the object to receive the augmentation.
4301 * @param {function} s the object that supplies the properties to augment.
4302 * @param {boolean} ov if true, properties already on the receiver
4303 * will be overwritten if found on the supplier.
4304 * @param {string[]} wl a whitelist. If supplied, only properties in
4305 * this list will be applied to the receiver.
4306 * @return {object} the extended object.
4308 Y.aggregate = function(r, s, ov, wl) {
4309 return Y.mix(r, s, ov, wl, 0, true);
4313 * Utility to set up the prototype, constructor and superclass properties to
4314 * support an inheritance strategy that can chain constructors and methods.
4315 * Static members will not be inherited.
4318 * @param {function} r the object to modify.
4319 * @param {function} s the object to inherit.
4320 * @param {object} px prototype properties to add/override.
4321 * @param {object} sx static properties to add/override.
4322 * @return {object} the extended object.
4324 Y.extend = function(r, s, px, sx) {
4326 Y.error('extend failed, verify dependencies');
4329 var sp = s.prototype, rp = Y.Object(sp);
4335 // assign constructor property
4336 if (s != Object && sp.constructor == OP.constructor) {
4340 // add prototype overrides
4342 Y.mix(rp, px, true);
4345 // add object overrides
4354 * Executes the supplied function for each item in
4355 * a collection. Supports arrays, objects, and
4358 * @param {object} o the object to iterate.
4359 * @param {function} f the function to execute. This function
4360 * receives the value, key, and object as parameters.
4361 * @param {object} c the execution context for the function.
4362 * @param {boolean} proto if true, prototype properties are
4363 * iterated on objects.
4364 * @return {YUI} the YUI instance.
4366 Y.each = function(o, f, c, proto) {
4367 return dispatch(o, f, c, proto, EACH);
4371 * Executes the supplied function for each item in
4372 * a collection. The operation stops if the function
4373 * returns true. Supports arrays, objects, and
4376 * @param {object} o the object to iterate.
4377 * @param {function} f the function to execute. This function
4378 * receives the value, key, and object as parameters.
4379 * @param {object} c the execution context for the function.
4380 * @param {boolean} proto if true, prototype properties are
4381 * iterated on objects.
4382 * @return {boolean} true if the function ever returns true,
4385 Y.some = function(o, f, c, proto) {
4386 return dispatch(o, f, c, proto, SOME);
4390 * Deep obj/array copy. Function clones are actually
4391 * wrappers around the original function.
4392 * Array-like objects are treated as arrays.
4393 * Primitives are returned untouched. Optionally, a
4394 * function can be provided to handle other data types,
4395 * filter keys, validate values, etc.
4398 * @param {object} o what to clone.
4399 * @param {boolean} safe if true, objects will not have prototype
4400 * items from the source. If false, they will. In this case, the
4401 * original is initially protected, but the clone is not completely
4402 * immune from changes to the source object prototype. Also, cloned
4403 * prototype items that are deleted from the clone will result
4404 * in the value of the source prototype being exposed. If operating
4405 * on a non-safe clone, items should be nulled out rather than deleted.
4406 * @param {function} f optional function to apply to each item in a
4407 * collection; it will be executed prior to applying the value to
4408 * the new object. Return false to prevent the copy.
4409 * @param {object} c optional execution context for f.
4410 * @param {object} owner Owner object passed when clone is iterating
4411 * an object. Used to set up context for cloned functions.
4412 * @param {object} cloned hash of previously cloned objects to avoid
4414 * @return {Array|Object} the cloned object.
4416 Y.clone = function(o, safe, f, c, owner, cloned) {
4418 if (!L.isObject(o)) {
4422 // @todo cloning YUI instances doesn't currently work
4423 if (Y.instanceOf(o, YUI)) {
4427 var o2, marked = cloned || {}, stamp,
4430 switch (L.type(o)) {
4434 // if we do this we need to set the flags too
4435 // return new RegExp(o.source);
4438 // o2 = Y.bind(o, owner);
4446 // #2528250 only one clone of a given object should be created.
4447 if (o[CLONE_MARKER]) {
4448 return marked[o[CLONE_MARKER]];
4453 o2 = (safe) ? {} : Y.Object(o);
4455 o[CLONE_MARKER] = stamp;
4459 // #2528250 don't try to clone element properties
4460 if (!o.addEventListener && !o.attachEvent) {
4461 yeach(o, function(v, k) {
4462 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
4463 if (k !== CLONE_MARKER) {
4464 if (k == 'prototype') {
4465 // skip the prototype
4466 // } else if (o[k] === o) {
4470 Y.clone(v, safe, f, c, owner || o, marked);
4478 Y.Object.each(marked, function(v, k) {
4479 if (v[CLONE_MARKER]) {
4481 delete v[CLONE_MARKER];
4483 v[CLONE_MARKER] = null;
4495 * Returns a function that will execute the supplied function in the
4496 * supplied object's context, optionally adding any additional
4497 * supplied parameters to the beginning of the arguments collection the
4498 * supplied to the function.
4501 * @param {Function|String} f the function to bind, or a function name
4502 * to execute on the context object.
4503 * @param {object} c the execution context.
4504 * @param {any} args* 0..n arguments to include before the arguments the
4505 * function is executed with.
4506 * @return {function} the wrapped function.
4508 Y.bind = function(f, c) {
4509 var xargs = arguments.length > 2 ?
4510 Y.Array(arguments, 2, true) : null;
4512 var fn = L.isString(f) ? c[f] : f,
4514 xargs.concat(Y.Array(arguments, 0, true)) : arguments;
4515 return fn.apply(c || fn, args);
4520 * Returns a function that will execute the supplied function in the
4521 * supplied object's context, optionally adding any additional
4522 * supplied parameters to the end of the arguments the function
4526 * @param {Function|String} f the function to bind, or a function name
4527 * to execute on the context object.
4528 * @param {object} c the execution context.
4529 * @param {any} args* 0..n arguments to append to the end of
4530 * arguments collection supplied to the function.
4531 * @return {function} the wrapped function.
4533 Y.rbind = function(f, c) {
4534 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
4536 var fn = L.isString(f) ? c[f] : f,
4538 Y.Array(arguments, 0, true).concat(xargs) : arguments;
4539 return fn.apply(c || fn, args);
4546 YUI.add('dom-base', function(Y) {
4550 * The DOM utility provides a cross-browser abtraction layer
4551 * normalizing DOM tasks, and adds extra helper functionality
4552 * for other common tasks.
4554 * @submodule dom-base
4560 * Provides DOM helper methods.
4564 var NODE_TYPE = 'nodeType',
4565 OWNER_DOCUMENT = 'ownerDocument',
4566 DOCUMENT_ELEMENT = 'documentElement',
4567 DEFAULT_VIEW = 'defaultView',
4568 PARENT_WINDOW = 'parentWindow',
4569 TAG_NAME = 'tagName',
4570 PARENT_NODE = 'parentNode',
4571 FIRST_CHILD = 'firstChild',
4572 PREVIOUS_SIBLING = 'previousSibling',
4573 NEXT_SIBLING = 'nextSibling',
4574 CONTAINS = 'contains',
4575 COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
4579 documentElement = Y.config.doc.documentElement,
4581 re_tag = /<([a-z]+)/i,
4583 createFromDIV = function(html, tag) {
4584 var div = Y.config.doc.createElement('div'),
4587 div.innerHTML = html;
4588 if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) {
4595 addFeature = Y.Features.add,
4596 testFeature = Y.Features.test,
4600 * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
4602 * @param {String} id the id attribute
4603 * @param {Object} doc optional The document to search. Defaults to current document
4604 * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
4606 byId: function(id, doc) {
4607 // handle dupe IDs and IE name collision
4608 return Y_DOM.allById(id, doc)[0] || null;
4612 * Returns the text content of the HTMLElement.
4614 * @param {HTMLElement} element The html element.
4615 * @return {String} The text content of the element (includes text of any descending elements).
4617 getText: (documentElement.textContent !== undefined) ?
4621 ret = element.textContent;
4624 } : function(element) {
4627 ret = element.innerText || element.nodeValue; // might be a textNode
4633 * Sets the text content of the HTMLElement.
4635 * @param {HTMLElement} element The html element.
4636 * @param {String} content The content to add.
4638 setText: (documentElement.textContent !== undefined) ?
4639 function(element, content) {
4641 element.textContent = content;
4643 } : function(element, content) {
4644 if ('innerText' in element) {
4645 element.innerText = content;
4646 } else if ('nodeValue' in element) {
4647 element.nodeValue = content;
4653 * Finds the ancestor of the element.
4655 * @param {HTMLElement} element The html element.
4656 * @param {Function} fn optional An optional boolean test to apply.
4657 * The optional function is passed the current DOM node being tested as its only argument.
4658 * If no function is given, the parentNode is returned.
4659 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
4660 * @return {HTMLElement | null} The matching DOM node or null if none found.
4662 ancestor: function(element, fn, testSelf) {
4665 ret = (!fn || fn(element)) ? element : null;
4668 return ret || Y_DOM.elementByAxis(element, PARENT_NODE, fn, null);
4672 * Finds the ancestors of the element.
4674 * @param {HTMLElement} element The html element.
4675 * @param {Function} fn optional An optional boolean test to apply.
4676 * The optional function is passed the current DOM node being tested as its only argument.
4677 * If no function is given, all ancestors are returned.
4678 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
4679 * @return {Array} An array containing all matching DOM nodes.
4681 ancestors: function(element, fn, testSelf) {
4682 var ancestor = Y_DOM.ancestor.apply(Y_DOM, arguments),
4683 ret = (ancestor) ? [ancestor] : [];
4685 while ((ancestor = Y_DOM.ancestor(ancestor, fn))) {
4687 ret.unshift(ancestor);
4695 * Searches the element by the given axis for the first matching element.
4696 * @method elementByAxis
4697 * @param {HTMLElement} element The html element.
4698 * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
4699 * @param {Function} fn optional An optional boolean test to apply.
4700 * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
4701 * The optional function is passed the current HTMLElement being tested as its only argument.
4702 * If no function is given, the first element is returned.
4703 * @return {HTMLElement | null} The matching element or null if none found.
4705 elementByAxis: function(element, axis, fn, all) {
4706 while (element && (element = element[axis])) { // NOTE: assignment
4707 if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
4715 * Determines whether or not one HTMLElement is or contains another HTMLElement.
4717 * @param {HTMLElement} element The containing html element.
4718 * @param {HTMLElement} needle The html element that may be contained.
4719 * @return {Boolean} Whether or not the element is or contains the needle.
4721 contains: function(element, needle) {
4724 if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
4726 } else if (element[CONTAINS]) {
4727 if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
4728 ret = element[CONTAINS](needle);
4730 ret = Y_DOM._bruteContains(element, needle);
4732 } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
4733 if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
4742 * Determines whether or not the HTMLElement is part of the document.
4744 * @param {HTMLElement} element The containing html element.
4745 * @param {HTMLElement} doc optional The document to check.
4746 * @return {Boolean} Whether or not the element is attached to the document.
4748 inDoc: function(element, doc) {
4752 if (element && element.nodeType) {
4753 (doc) || (doc = element[OWNER_DOCUMENT]);
4755 rootNode = doc[DOCUMENT_ELEMENT];
4757 // contains only works with HTML_ELEMENT
4758 if (rootNode && rootNode.contains && element.tagName) {
4759 ret = rootNode.contains(element);
4761 ret = Y_DOM.contains(rootNode, element);
4769 allById: function(id, root) {
4770 root = root || Y.config.doc;
4776 if (root.querySelectorAll) {
4777 ret = root.querySelectorAll('[id="' + id + '"]');
4778 } else if (root.all) {
4779 nodes = root.all(id);
4782 // root.all may return HTMLElement or HTMLCollection.
4783 // some elements are also HTMLCollection (FORM, SELECT).
4784 if (nodes.nodeName) {
4785 if (nodes.id === id) { // avoid false positive on name
4787 nodes = EMPTY_ARRAY; // done, no need to filter
4788 } else { // prep for filtering
4794 // filter out matches on node.name
4795 // and element.id as reference to element with id === 'id'
4796 for (i = 0; node = nodes[i++];) {
4797 if (node.id === id ||
4798 (node.attributes && node.attributes.id &&
4799 node.attributes.id.value === id)) {
4806 ret = [Y_DOM._getDoc(root).getElementById(id)];
4813 * Creates a new dom node using the provided markup string.
4815 * @param {String} html The markup used to create the element
4816 * @param {HTMLDocument} doc An optional document context
4817 * @return {HTMLElement|DocumentFragment} returns a single HTMLElement
4818 * when creating one node, and a documentFragment when creating
4821 create: function(html, doc) {
4822 if (typeof html === 'string') {
4823 html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
4827 doc = doc || Y.config.doc;
4828 var m = re_tag.exec(html),
4829 create = Y_DOM._create,
4830 custom = Y_DOM.creators,
4835 if (html != undefined) { // not undefined or null
4837 creator = custom[m[1].toLowerCase()];
4838 if (typeof creator === 'function') {
4845 nodes = create(html, doc, tag).childNodes;
4847 if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
4848 ret = nodes[0].parentNode.removeChild(nodes[0]);
4849 } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
4850 if (nodes.length === 2) {
4851 ret = nodes[0].nextSibling;
4853 nodes[0].parentNode.removeChild(nodes[0]);
4854 ret = Y_DOM._nl2frag(nodes, doc);
4856 } else { // return multiple nodes as a fragment
4857 ret = Y_DOM._nl2frag(nodes, doc);
4864 _nl2frag: function(nodes, doc) {
4868 if (nodes && (nodes.push || nodes.item) && nodes[0]) {
4869 doc = doc || nodes[0].ownerDocument;
4870 ret = doc.createDocumentFragment();
4872 if (nodes.item) { // convert live list to static array
4873 nodes = Y.Array(nodes, 0, true);
4876 for (i = 0, len = nodes.length; i < len; i++) {
4877 ret.appendChild(nodes[i]);
4879 } // else inline with log for minification
4884 CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
4886 'class': 'className'
4889 'className': 'class'
4893 * Provides a normalized attribute interface.
4894 * @method setAttibute
4895 * @param {HTMLElement} el The target element for the attribute.
4896 * @param {String} attr The attribute to set.
4897 * @param {String} val The value of the attribute.
4899 setAttribute: function(el, attr, val, ieAttr) {
4900 if (el && attr && el.setAttribute) {
4901 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
4902 el.setAttribute(attr, val, ieAttr);
4908 * Provides a normalized attribute interface.
4909 * @method getAttibute
4910 * @param {HTMLElement} el The target element for the attribute.
4911 * @param {String} attr The attribute to get.
4912 * @return {String} The current value of the attribute.
4914 getAttribute: function(el, attr, ieAttr) {
4915 ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
4917 if (el && attr && el.getAttribute) {
4918 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
4919 ret = el.getAttribute(attr, ieAttr);
4922 ret = ''; // per DOM spec
4928 isWindow: function(obj) {
4929 return !!(obj && obj.alert && obj.document);
4934 _create: function(html, doc, tag) {
4937 var frag = Y_DOM._fragClones[tag];
4939 frag = frag.cloneNode(false);
4941 frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
4943 frag.innerHTML = html;
4947 _removeChildNodes: function(node) {
4948 while (node.firstChild) {
4949 node.removeChild(node.firstChild);
4954 * Inserts content in a node at the given location
4956 * @param {HTMLElement} node The node to insert into
4957 * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted
4958 * @param {HTMLElement} where Where to insert the content
4959 * If no "where" is given, content is appended to the node
4960 * Possible values for "where"
4962 * <dt>HTMLElement</dt>
4963 * <dd>The element to insert before</dd>
4964 * <dt>"replace"</dt>
4965 * <dd>Replaces the existing HTML</dd>
4967 * <dd>Inserts before the existing HTML</dd>
4969 * <dd>Inserts content before the node</dd>
4971 * <dd>Inserts content after the node</dd>
4974 addHTML: function(node, content, where) {
4975 var nodeParent = node.parentNode,
4982 if (content != undefined) { // not null or undefined (maybe 0)
4983 if (content.nodeType) { // DOM node, just add it
4985 } else if (typeof content == 'string' || typeof content == 'number') {
4986 ret = newNode = Y_DOM.create(content);
4987 } else if (content[0] && content[0].nodeType) { // array or collection
4988 newNode = Y.config.doc.createDocumentFragment();
4989 while ((item = content[i++])) {
4990 newNode.appendChild(item); // append to fragment for insertion
4996 if (where.nodeType) { // insert regardless of relationship to node
4997 where.parentNode.insertBefore(newNode, where);
5001 while (node.firstChild) {
5002 node.removeChild(node.firstChild);
5004 if (newNode) { // allow empty content to clear node
5005 node.appendChild(newNode);
5009 nodeParent.insertBefore(newNode, node);
5012 if (node.nextSibling) { // IE errors if refNode is null
5013 nodeParent.insertBefore(newNode, node.nextSibling);
5015 nodeParent.appendChild(newNode);
5019 node.appendChild(newNode);
5022 } else if (newNode) {
5023 node.appendChild(newNode);
5033 getValue: function(node) {
5034 var ret = '', // TODO: return null?
5037 if (node && node[TAG_NAME]) {
5038 getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
5047 // workaround for IE8 JSON stringify bug
5048 // which converts empty string values to null
5049 if (ret === EMPTY_STRING) {
5050 ret = EMPTY_STRING; // for real
5053 return (typeof ret === 'string') ? ret : '';
5056 setValue: function(node, val) {
5059 if (node && node[TAG_NAME]) {
5060 setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
5070 siblings: function(node, fn) {
5074 while ((sibling = sibling[PREVIOUS_SIBLING])) {
5075 if (sibling[TAG_NAME] && (!fn || fn(sibling))) {
5076 nodes.unshift(sibling);
5081 while ((sibling = sibling[NEXT_SIBLING])) {
5082 if (sibling[TAG_NAME] && (!fn || fn(sibling))) {
5083 nodes.push(sibling);
5091 * Brute force version of contains.
5092 * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
5093 * @method _bruteContains
5095 * @param {HTMLElement} element The containing html element.
5096 * @param {HTMLElement} needle The html element that may be contained.
5097 * @return {Boolean} Whether or not the element is or contains the needle.
5099 _bruteContains: function(element, needle) {
5101 if (element === needle) {
5104 needle = needle.parentNode;
5109 // TODO: move to Lang?
5111 * Memoizes dynamic regular expressions to boost runtime performance.
5112 * @method _getRegExp
5114 * @param {String} str The string to convert to a regular expression.
5115 * @param {String} flags optional An optinal string of flags.
5116 * @return {RegExp} An instance of RegExp
5118 _getRegExp: function(str, flags) {
5119 flags = flags || '';
5120 Y_DOM._regexCache = Y_DOM._regexCache || {};
5121 if (!Y_DOM._regexCache[str + flags]) {
5122 Y_DOM._regexCache[str + flags] = new RegExp(str, flags);
5124 return Y_DOM._regexCache[str + flags];
5127 // TODO: make getDoc/Win true privates?
5129 * returns the appropriate document.
5132 * @param {HTMLElement} element optional Target element.
5133 * @return {Object} The document for the given element or the default document.
5135 _getDoc: function(element) {
5136 var doc = Y.config.doc;
5138 doc = (element[NODE_TYPE] === 9) ? element : // element === document
5139 element[OWNER_DOCUMENT] || // element === DOM node
5140 element.document || // element === window
5141 Y.config.doc; // default
5148 * returns the appropriate window.
5151 * @param {HTMLElement} element optional Target element.
5152 * @return {Object} The window for the given element or the default window.
5154 _getWin: function(element) {
5155 var doc = Y_DOM._getDoc(element);
5156 return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
5159 _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
5160 fn = (typeof fn === 'string') ? Y_DOM[fn] : fn;
5162 args = Array.prototype.slice.call(arguments, 2),
5168 while ((node = nodes[i++])) {
5169 result = result = fn.call(Y_DOM, node, arg1, arg2, arg3, etc);
5170 if (typeof result !== 'undefined') {
5171 (ret) || (ret = []);
5177 return (typeof ret !== 'undefined') ? ret : nodes;
5180 wrap: function(node, html) {
5181 var parent = Y.DOM.create(html),
5182 nodes = parent.getElementsByTagName('*');
5185 parent = nodes[nodes.length - 1];
5188 if (node.parentNode) {
5189 node.parentNode.replaceChild(parent, node);
5191 parent.appendChild(node);
5194 unwrap: function(node) {
5195 var parent = node.parentNode,
5196 lastChild = parent.lastChild,
5197 node = parent.firstChild,
5202 grandparent = parent.parentNode;
5204 while (node !== lastChild) {
5205 next = node.nextSibling;
5206 grandparent.insertBefore(node, parent);
5209 grandparent.replaceChild(lastChild, parent);
5211 parent.removeChild(node);
5216 generateID: function(el) {
5230 addFeature('innerhtml', 'table', {
5232 var node = Y.config.doc.createElement('table');
5234 node.innerHTML = '<tbody></tbody>';
5238 return (node.firstChild && node.firstChild.nodeName === 'TBODY');
5242 addFeature('innerhtml-div', 'tr', {
5244 return createFromDIV('<tr></tr>', 'tr');
5248 addFeature('innerhtml-div', 'script', {
5250 return createFromDIV('<script></script>', 'script');
5254 addFeature('value-set', 'select', {
5256 var node = Y.config.doc.createElement('select');
5257 node.innerHTML = '<option>1</option><option>2</option>';
5259 return (node.value && node.value === '2');
5264 var creators = Y_DOM.creators,
5265 create = Y_DOM.create,
5266 re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
5268 TABLE_OPEN = '<table>',
5269 TABLE_CLOSE = '</table>';
5271 if (!testFeature('innerhtml', 'table')) {
5272 // TODO: thead/tfoot with nested tbody
5273 // IE adds TBODY when creating TABLE elements (which may share this impl)
5274 creators.tbody = function(html, doc) {
5275 var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
5276 tb = frag.children.tags('tbody')[0];
5278 if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
5279 tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
5285 if (!testFeature('innerhtml-div', 'script')) {
5286 creators.script = function(html, doc) {
5287 var frag = doc.createElement('div');
5289 frag.innerHTML = '-' + html;
5290 frag.removeChild(frag[FIRST_CHILD]);
5294 Y_DOM.creators.link = Y_DOM.creators.style = Y_DOM.creators.script;
5298 if (!testFeature('value-set', 'select')) {
5299 Y_DOM.VALUE_SETTERS.select = function(node, val) {
5300 for (var i = 0, options = node.getElementsByTagName('option'), option;
5301 option = options[i++];) {
5302 if (Y_DOM.getValue(option) === val) {
5303 option.selected = true;
5304 //Y_DOM.setAttribute(option, 'selected', 'selected');
5311 Y.mix(Y_DOM.VALUE_GETTERS, {
5312 button: function(node) {
5313 return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
5317 Y.mix(Y_DOM.VALUE_SETTERS, {
5318 // IE: node.value changes the button text, which should be handled via innerHTML
5319 button: function(node, val) {
5320 var attr = node.attributes.value;
5322 attr = node[OWNER_DOCUMENT].createAttribute('value');
5323 node.setAttributeNode(attr);
5331 if (!testFeature('innerhtml-div', 'tr')) {
5333 option: function(html, doc) {
5334 return create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
5337 tr: function(html, doc) {
5338 return create('<tbody>' + html + '</tbody>', doc);
5341 td: function(html, doc) {
5342 return create('<tr>' + html + '</tr>', doc);
5345 col: function(html, doc) {
5346 return create('<colgroup>' + html + '</colgroup>', doc);
5355 thead: creators.tbody,
5356 tfoot: creators.tbody,
5357 caption: creators.tbody,
5358 colgroup: creators.tbody,
5359 optgroup: creators.option
5363 Y.mix(Y_DOM.VALUE_GETTERS, {
5364 option: function(node) {
5365 var attrs = node.attributes;
5366 return (attrs.value && attrs.value.specified) ? node.value : node.text;
5369 select: function(node) {
5370 var val = node.value,
5371 options = node.options;
5373 if (options && options.length) {
5374 // TODO: implement multipe select
5375 if (node.multiple) {
5377 val = Y_DOM.getValue(options[node.selectedIndex]);
5388 var addClass, hasClass, removeClass;
5392 * Determines whether a DOM element has the given className.
5395 * @param {HTMLElement} element The DOM element.
5396 * @param {String} className the class name to search for
5397 * @return {Boolean} Whether or not the element has the given class.
5399 hasClass: function(node, className) {
5400 var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
5401 return re.test(node.className);
5405 * Adds a class name to a given DOM element.
5408 * @param {HTMLElement} element The DOM element.
5409 * @param {String} className the class name to add to the class attribute
5411 addClass: function(node, className) {
5412 if (!Y.DOM.hasClass(node, className)) { // skip if already present
5413 node.className = Y.Lang.trim([node.className, className].join(' '));
5418 * Removes a class name from a given element.
5419 * @method removeClass
5421 * @param {HTMLElement} element The DOM element.
5422 * @param {String} className the class name to remove from the class attribute
5424 removeClass: function(node, className) {
5425 if (className && hasClass(node, className)) {
5426 node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
5427 className + '(?:\\s+|$)'), ' '));
5429 if ( hasClass(node, className) ) { // in case of multiple adjacent
5430 removeClass(node, className);
5436 * Replace a class with another class for a given element.
5437 * If no oldClassName is present, the newClassName is simply added.
5438 * @method replaceClass
5440 * @param {HTMLElement} element The DOM element
5441 * @param {String} oldClassName the class name to be replaced
5442 * @param {String} newClassName the class name that will be replacing the old class name
5444 replaceClass: function(node, oldC, newC) {
5445 removeClass(node, oldC); // remove first in case oldC === newC
5446 addClass(node, newC);
5450 * If the className exists on the node it is removed, if it doesn't exist it is added.
5451 * @method toggleClass
5453 * @param {HTMLElement} element The DOM element
5454 * @param {String} className the class name to be toggled
5455 * @param {Boolean} addClass optional boolean to indicate whether class
5456 * should be added or removed regardless of current state
5458 toggleClass: function(node, className, force) {
5459 var add = (force !== undefined) ? force :
5460 !(hasClass(node, className));
5463 addClass(node, className);
5465 removeClass(node, className);
5470 hasClass = Y.DOM.hasClass;
5471 removeClass = Y.DOM.removeClass;
5472 addClass = Y.DOM.addClass;
5476 * Sets the width of the element to the given size, regardless
5477 * of box model, border, padding, etc.
5479 * @param {HTMLElement} element The DOM element.
5480 * @param {String|Int} size The pixel height to size to
5483 setWidth: function(node, size) {
5484 Y.DOM._setSize(node, 'width', size);
5488 * Sets the height of the element to the given size, regardless
5489 * of box model, border, padding, etc.
5491 * @param {HTMLElement} element The DOM element.
5492 * @param {String|Int} size The pixel height to size to
5495 setHeight: function(node, size) {
5496 Y.DOM._setSize(node, 'height', size);
5499 _setSize: function(node, prop, val) {
5500 val = (val > 0) ? val : 0;
5503 node.style[prop] = val + 'px';
5504 size = (prop === 'height') ? node.offsetHeight : node.offsetWidth;
5507 val = val - (size - val);
5513 node.style[prop] = val + 'px';
5519 }, '3.3.0' ,{requires:['oop']});
5520 YUI.add('dom-style', function(Y) {
5524 * Add style management functionality to DOM.
5526 * @submodule dom-style
5530 var DOCUMENT_ELEMENT = 'documentElement',
5531 DEFAULT_VIEW = 'defaultView',
5532 OWNER_DOCUMENT = 'ownerDocument',
5535 CSS_FLOAT = 'cssFloat',
5536 STYLE_FLOAT = 'styleFloat',
5537 TRANSPARENT = 'transparent',
5538 GET_COMPUTED_STYLE = 'getComputedStyle',
5539 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
5541 WINDOW = Y.config.win,
5542 DOCUMENT = Y.config.doc,
5543 UNDEFINED = undefined,
5547 TRANSFORM = 'transform',
5548 VENDOR_TRANSFORM = [
5554 re_color = /color$/i,
5555 re_unit = /width|height|top|left|right|bottom|margin|padding/i;
5557 Y.Array.each(VENDOR_TRANSFORM, function(val) {
5558 if (val in DOCUMENT[DOCUMENT_ELEMENT].style) {
5571 * Sets a style property for a given element.
5573 * @param {HTMLElement} An HTMLElement to apply the style to.
5574 * @param {String} att The style property to set.
5575 * @param {String|Number} val The value.
5577 setStyle: function(node, att, val, style) {
5578 style = style || node.style;
5579 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES;
5582 if (val === null || val === '') { // normalize unsetting
5584 } else if (!isNaN(new Number(val)) && re_unit.test(att)) { // number values may need a unit
5585 val += Y_DOM.DEFAULT_UNIT;
5588 if (att in CUSTOM_STYLES) {
5589 if (CUSTOM_STYLES[att].set) {
5590 CUSTOM_STYLES[att].set(node, val, style);
5591 return; // NOTE: return
5592 } else if (typeof CUSTOM_STYLES[att] === 'string') {
5593 att = CUSTOM_STYLES[att];
5595 } else if (att === '') { // unset inline styles
5604 * Returns the current style value for the given property.
5606 * @param {HTMLElement} An HTMLElement to get the style from.
5607 * @param {String} att The style property to get.
5609 getStyle: function(node, att, style) {
5610 style = style || node.style;
5611 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES,
5615 if (att in CUSTOM_STYLES) {
5616 if (CUSTOM_STYLES[att].get) {
5617 return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
5618 } else if (typeof CUSTOM_STYLES[att] === 'string') {
5619 att = CUSTOM_STYLES[att];
5623 if (val === '') { // TODO: is empty string sufficient?
5624 val = Y_DOM[GET_COMPUTED_STYLE](node, att);
5632 * Sets multiple style properties.
5634 * @param {HTMLElement} node An HTMLElement to apply the styles to.
5635 * @param {Object} hash An object literal of property:value pairs.
5637 setStyles: function(node, hash) {
5638 var style = node.style;
5639 Y.each(hash, function(v, n) {
5640 Y_DOM.setStyle(node, n, v, style);
5645 * Returns the computed style for the given node.
5646 * @method getComputedStyle
5647 * @param {HTMLElement} An HTMLElement to get the style from.
5648 * @param {String} att The style property to get.
5649 * @return {String} The computed value of the style property.
5651 getComputedStyle: function(node, att) {
5653 doc = node[OWNER_DOCUMENT];
5655 if (node[STYLE] && doc[DEFAULT_VIEW] && doc[DEFAULT_VIEW][GET_COMPUTED_STYLE]) {
5656 val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
5662 // normalize reserved word float alternatives ("cssFloat" or "styleFloat")
5663 if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
5664 Y_DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
5665 } else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
5666 Y_DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
5669 // fix opera computedStyle default color unit (convert to rgb)
5671 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) {
5672 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
5673 val = view[GET_COMPUTED_STYLE](node, '')[att];
5675 if (re_color.test(att)) {
5676 val = Y.Color.toRGB(val);
5684 // safari converts transparent to rgba(), others use "transparent"
5686 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) {
5687 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
5688 val = view[GET_COMPUTED_STYLE](node, '')[att];
5690 if (val === 'rgba(0, 0, 0, 0)') {
5699 Y.DOM._getAttrOffset = function(node, attr) {
5700 var val = Y.DOM[GET_COMPUTED_STYLE](node, attr),
5701 offsetParent = node.offsetParent,
5706 if (val === 'auto') {
5707 position = Y.DOM.getStyle(node, 'position');
5708 if (position === 'static' || position === 'relative') {
5710 } else if (offsetParent && offsetParent[GET_BOUNDING_CLIENT_RECT]) {
5711 parentOffset = offsetParent[GET_BOUNDING_CLIENT_RECT]()[attr];
5712 offset = node[GET_BOUNDING_CLIENT_RECT]()[attr];
5713 if (attr === 'left' || attr === 'top') {
5714 val = offset - parentOffset;
5716 val = parentOffset - node[GET_BOUNDING_CLIENT_RECT]()[attr];
5724 Y.DOM._getOffset = function(node) {
5729 pos = Y_DOM.getStyle(node, 'position');
5731 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'left'), 10),
5732 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'top'), 10)
5735 if ( isNaN(xy[0]) ) { // in case of 'auto'
5736 xy[0] = parseInt(Y_DOM.getStyle(node, 'left'), 10); // try inline
5737 if ( isNaN(xy[0]) ) { // default to offset value
5738 xy[0] = (pos === 'relative') ? 0 : node.offsetLeft || 0;
5742 if ( isNaN(xy[1]) ) { // in case of 'auto'
5743 xy[1] = parseInt(Y_DOM.getStyle(node, 'top'), 10); // try inline
5744 if ( isNaN(xy[1]) ) { // default to offset value
5745 xy[1] = (pos === 'relative') ? 0 : node.offsetTop || 0;
5754 Y_DOM.CUSTOM_STYLES.transform = {
5755 set: function(node, val, style) {
5756 style[TRANSFORM] = val;
5759 get: function(node, style) {
5760 return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORM);
5767 var PARSE_INT = parseInt,
5790 re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
5791 re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
5792 re_hex3: /([0-9A-F])/gi,
5794 toRGB: function(val) {
5795 if (!Y.Color.re_RGB.test(val)) {
5796 val = Y.Color.toHex(val);
5799 if(Y.Color.re_hex.exec(val)) {
5801 PARSE_INT(RE.$1, 16),
5802 PARSE_INT(RE.$2, 16),
5803 PARSE_INT(RE.$3, 16)
5809 toHex: function(val) {
5810 val = Y.Color.KEYWORDS[val] || val;
5811 if (Y.Color.re_RGB.exec(val)) {
5813 Number(RE.$1).toString(16),
5814 Number(RE.$2).toString(16),
5815 Number(RE.$3).toString(16)
5818 for (var i = 0; i < val.length; i++) {
5819 if (val[i].length < 2) {
5820 val[i] = '0' + val[i];
5827 if (val.length < 6) {
5828 val = val.replace(Y.Color.re_hex3, '$1$1');
5831 if (val !== 'transparent' && val.indexOf('#') < 0) {
5835 return val.toUpperCase();
5842 }, '3.3.0' ,{requires:['dom-base']});
5843 YUI.add('dom-screen', function(Y) {
5848 * Adds position and region management functionality to DOM.
5850 * @submodule dom-screen
5854 var DOCUMENT_ELEMENT = 'documentElement',
5855 COMPAT_MODE = 'compatMode',
5856 POSITION = 'position',
5858 RELATIVE = 'relative',
5861 _BACK_COMPAT = 'BackCompat',
5863 BORDER_LEFT_WIDTH = 'borderLeftWidth',
5864 BORDER_TOP_WIDTH = 'borderTopWidth',
5865 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
5866 GET_COMPUTED_STYLE = 'getComputedStyle',
5870 // TODO: how about thead/tbody/tfoot/tr?
5871 // TODO: does caption matter?
5872 RE_TABLE = /^t(?:able|d|h)$/i,
5877 if (Y.config.doc[COMPAT_MODE] !== 'quirks') {
5878 SCROLL_NODE = DOCUMENT_ELEMENT;
5880 SCROLL_NODE = 'body';
5886 * Returns the inner height of the viewport (exludes scrollbar).
5888 * @return {Number} The current height of the viewport.
5890 winHeight: function(node) {
5891 var h = Y_DOM._getWinSize(node).height;
5896 * Returns the inner width of the viewport (exludes scrollbar).
5898 * @return {Number} The current width of the viewport.
5900 winWidth: function(node) {
5901 var w = Y_DOM._getWinSize(node).width;
5908 * @return {Number} The current height of the document.
5910 docHeight: function(node) {
5911 var h = Y_DOM._getDocSize(node).height;
5912 return Math.max(h, Y_DOM._getWinSize(node).height);
5918 * @return {Number} The current width of the document.
5920 docWidth: function(node) {
5921 var w = Y_DOM._getDocSize(node).width;
5922 return Math.max(w, Y_DOM._getWinSize(node).width);
5926 * Amount page has been scroll horizontally
5927 * @method docScrollX
5928 * @return {Number} The current amount the screen is scrolled horizontally.
5930 docScrollX: function(node, doc) {
5931 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
5932 var dv = doc.defaultView,
5933 pageOffset = (dv) ? dv.pageXOffset : 0;
5934 return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset);
5938 * Amount page has been scroll vertically
5939 * @method docScrollY
5940 * @return {Number} The current amount the screen is scrolled vertically.
5942 docScrollY: function(node, doc) {
5943 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
5944 var dv = doc.defaultView,
5945 pageOffset = (dv) ? dv.pageYOffset : 0;
5946 return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset);
5950 * Gets the current position of an element based on page coordinates.
5951 * Element must be part of the DOM tree to have page coordinates
5952 * (display:none or elements not appended return false).
5954 * @param element The target element
5955 * @return {Array} The XY position of the element
5957 TODO: test inDocument/display?
5960 if (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
5961 return function(node) {
5973 if (node && node.tagName) {
5974 doc = node.ownerDocument;
5975 rootNode = doc[DOCUMENT_ELEMENT];
5977 // inline inDoc check for perf
5978 if (rootNode.contains) {
5979 inDoc = rootNode.contains(node);
5981 inDoc = Y.DOM.contains(rootNode, node);
5985 scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc);
5986 scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc);
5987 box = node[GET_BOUNDING_CLIENT_RECT]();
5988 xy = [box.left, box.top];
5993 mode = doc[COMPAT_MODE];
5994 bLeft = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
5995 bTop = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
5997 if (Y.UA.ie === 6) {
5998 if (mode !== _BACK_COMPAT) {
6004 if ((mode == _BACK_COMPAT)) {
6005 if (bLeft !== MEDIUM) {
6006 off1 = parseInt(bLeft, 10);
6008 if (bTop !== MEDIUM) {
6009 off2 = parseInt(bTop, 10);
6018 if ((scrollTop || scrollLeft)) {
6020 xy[0] += scrollLeft;
6026 xy = Y_DOM._getOffset(node);
6032 return function(node) { // manually calculate by crawling up offsetParents
6033 //Calculate the Top and Left border sizes (assumes pixels)
6042 if (Y_DOM.inDoc(node)) {
6043 xy = [node.offsetLeft, node.offsetTop];
6044 doc = node.ownerDocument;
6046 // TODO: refactor with !! or just falsey
6047 bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
6049 // TODO: worth refactoring for TOP/LEFT only?
6050 while ((parentNode = parentNode.offsetParent)) {
6051 xy[0] += parentNode.offsetLeft;
6052 xy[1] += parentNode.offsetTop;
6054 xy = Y_DOM._calcBorders(parentNode, xy);
6058 // account for any scrolled ancestors
6059 if (Y_DOM.getStyle(node, POSITION) != FIXED) {
6062 while ((parentNode = parentNode.parentNode)) {
6063 scrollTop = parentNode.scrollTop;
6064 scrollLeft = parentNode.scrollLeft;
6066 //Firefox does something funky with borders when overflow is not visible.
6067 if (Y.UA.gecko && (Y_DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
6068 xy = Y_DOM._calcBorders(parentNode, xy);
6072 if (scrollTop || scrollLeft) {
6073 xy[0] -= scrollLeft;
6077 xy[0] += Y_DOM.docScrollX(node, doc);
6078 xy[1] += Y_DOM.docScrollY(node, doc);
6081 //Fix FIXED position -- add scrollbars
6082 xy[0] += Y_DOM.docScrollX(node, doc);
6083 xy[1] += Y_DOM.docScrollY(node, doc);
6086 xy = Y_DOM._getOffset(node);
6093 }(),// NOTE: Executing for loadtime branching
6096 * Gets the current X position of an element based on page coordinates.
6097 * Element must be part of the DOM tree to have page coordinates
6098 * (display:none or elements not appended return false).
6100 * @param element The target element
6101 * @return {Int} The X position of the element
6104 getX: function(node) {
6105 return Y_DOM.getXY(node)[0];
6109 * Gets the current Y position of an element based on page coordinates.
6110 * Element must be part of the DOM tree to have page coordinates
6111 * (display:none or elements not appended return false).
6113 * @param element The target element
6114 * @return {Int} The Y position of the element
6117 getY: function(node) {
6118 return Y_DOM.getXY(node)[1];
6122 * Set the position of an html element in page coordinates.
6123 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6125 * @param element The target element
6126 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
6127 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
6129 setXY: function(node, xy, noRetry) {
6130 var setStyle = Y_DOM.setStyle,
6137 pos = Y_DOM.getStyle(node, POSITION);
6139 delta = Y_DOM._getOffset(node);
6140 if (pos == 'static') { // default to relative
6142 setStyle(node, POSITION, pos);
6144 currentXY = Y_DOM.getXY(node);
6146 if (xy[0] !== null) {
6147 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
6150 if (xy[1] !== null) {
6151 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
6155 newXY = Y_DOM.getXY(node);
6156 if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
6157 Y_DOM.setXY(node, xy, true);
6166 * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
6167 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6169 * @param element The target element
6170 * @param {Int} x The X values for new position (coordinates are page-based)
6172 setX: function(node, x) {
6173 return Y_DOM.setXY(node, [x, null]);
6177 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
6178 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6180 * @param element The target element
6181 * @param {Int} y The Y values for new position (coordinates are page-based)
6183 setY: function(node, y) {
6184 return Y_DOM.setXY(node, [null, y]);
6189 * @description Swap the xy position with another node
6190 * @param {Node} node The node to swap with
6191 * @param {Node} otherNode The other node to swap with
6194 swapXY: function(node, otherNode) {
6195 var xy = Y_DOM.getXY(node);
6196 Y_DOM.setXY(node, Y_DOM.getXY(otherNode));
6197 Y_DOM.setXY(otherNode, xy);
6200 _calcBorders: function(node, xy2) {
6201 var t = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
6202 l = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
6204 if (RE_TABLE.test(node.tagName)) {
6214 _getWinSize: function(node, doc) {
6215 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc;
6216 var win = doc.defaultView || doc.parentWindow,
6217 mode = doc[COMPAT_MODE],
6218 h = win.innerHeight,
6220 root = doc[DOCUMENT_ELEMENT];
6222 if ( mode && !Y.UA.opera ) { // IE, Gecko
6223 if (mode != 'CSS1Compat') { // Quirks
6226 h = root.clientHeight;
6227 w = root.clientWidth;
6229 return { height: h, width: w };
6232 _getDocSize: function(node) {
6233 var doc = (node) ? Y_DOM._getDoc(node) : Y.config.doc,
6234 root = doc[DOCUMENT_ELEMENT];
6236 if (doc[COMPAT_MODE] != 'CSS1Compat') {
6240 return { height: root.scrollHeight, width: root.scrollWidth };
6251 getOffsets = function(r1, r2) {
6252 var t = Math.max(r1[TOP], r2[TOP]),
6253 r = Math.min(r1[RIGHT], r2[RIGHT]),
6254 b = Math.min(r1[BOTTOM], r2[BOTTOM]),
6255 l = Math.max(r1[LEFT], r2[LEFT]),
6269 * Returns an Object literal containing the following about this element: (top, right, bottom, left)
6272 * @param {HTMLElement} element The DOM element.
6273 @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
6275 region: function(node) {
6276 var xy = DOM.getXY(node),
6280 ret = DOM._getRegion(
6282 xy[0] + node.offsetWidth, // right
6283 xy[1] + node.offsetHeight, // bottom
6292 * Find the intersect information for the passes nodes.
6295 * @param {HTMLElement} element The first element
6296 * @param {HTMLElement | Object} element2 The element or region to check the interect with
6297 * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
6298 @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
6300 intersect: function(node, node2, altRegion) {
6301 var r = altRegion || DOM.region(node), region = {},
6306 region = DOM.region(n);
6307 } else if (Y.Lang.isObject(node2)) {
6313 off = getOffsets(region, r);
6317 bottom: off[BOTTOM],
6319 area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
6320 yoff: ((off[BOTTOM] - off[TOP])),
6321 xoff: (off[RIGHT] - off[LEFT]),
6322 inRegion: DOM.inRegion(node, node2, false, altRegion)
6327 * Check if any part of this node is in the passed region
6330 * @param {Object} node2 The node to get the region from or an Object literal of the region
6331 * $param {Boolean} all Should all of the node be inside the region
6332 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
6333 * @return {Boolean} True if in region, false if not.
6335 inRegion: function(node, node2, all, altRegion) {
6337 r = altRegion || DOM.region(node),
6342 region = DOM.region(n);
6343 } else if (Y.Lang.isObject(node2)) {
6351 r[LEFT] >= region[LEFT] &&
6352 r[RIGHT] <= region[RIGHT] &&
6353 r[TOP] >= region[TOP] &&
6354 r[BOTTOM] <= region[BOTTOM] );
6356 off = getOffsets(region, r);
6357 if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
6367 * Check if any part of this element is in the viewport
6368 * @method inViewportRegion
6370 * @param {HTMLElement} element The DOM element.
6371 * @param {Boolean} all Should all of the node be inside the region
6372 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
6373 * @return {Boolean} True if in region, false if not.
6375 inViewportRegion: function(node, all, altRegion) {
6376 return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
6380 _getRegion: function(t, r, b, l) {
6383 region[TOP] = region[1] = t;
6384 region[LEFT] = region[0] = l;
6387 region.width = region[RIGHT] - region[LEFT];
6388 region.height = region[BOTTOM] - region[TOP];
6394 * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
6395 * @method viewportRegion
6397 * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
6399 viewportRegion: function(node) {
6400 node = node || Y.config.doc.documentElement;
6406 scrollX = DOM.docScrollX(node);
6407 scrollY = DOM.docScrollY(node);
6409 ret = DOM._getRegion(scrollY, // top
6410 DOM.winWidth(node) + scrollX, // right
6411 scrollY + DOM.winHeight(node), // bottom
6421 }, '3.3.0' ,{requires:['dom-base', 'dom-style', 'event-base']});
6422 YUI.add('selector-native', function(Y) {
6426 * The selector-native module provides support for native querySelector
6428 * @submodule selector-native
6433 * Provides support for using CSS selectors to query the DOM
6439 Y.namespace('Selector'); // allow native module to standalone
6441 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
6442 OWNER_DOCUMENT = 'ownerDocument';
6449 _compare: ('sourceIndex' in Y.config.doc.documentElement) ?
6450 function(nodeA, nodeB) {
6451 var a = nodeA.sourceIndex,
6452 b = nodeB.sourceIndex;
6462 } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ?
6463 function(nodeA, nodeB) {
6464 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
6470 function(nodeA, nodeB) {
6471 var rangeA, rangeB, compare;
6472 if (nodeA && nodeB) {
6473 rangeA = nodeA[OWNER_DOCUMENT].createRange();
6474 rangeA.setStart(nodeA, 0);
6475 rangeB = nodeB[OWNER_DOCUMENT].createRange();
6476 rangeB.setStart(nodeB, 0);
6477 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
6484 _sort: function(nodes) {
6486 nodes = Y.Array(nodes, 0, true);
6488 nodes.sort(Selector._compare);
6495 _deDupe: function(nodes) {
6499 for (i = 0; (node = nodes[i++]);) {
6501 ret[ret.length] = node;
6506 for (i = 0; (node = ret[i++]);) {
6508 node.removeAttribute('_found');
6515 * Retrieves a set of nodes based on a given CSS selector.
6518 * @param {string} selector The CSS Selector to test the node against.
6519 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
6520 * @param {Boolean} firstOnly optional Whether or not to return only the first match.
6521 * @return {Array} An array of nodes that match the given selector.
6524 query: function(selector, root, firstOnly, skipNative) {
6525 root = root || Y.config.doc;
6527 useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative),
6528 queries = [[selector, root]],
6532 fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
6534 if (selector && fn) {
6535 // split group into seperate queries
6536 if (!skipNative && // already done if skipping
6537 (!useNative || root.tagName)) { // split native when element scoping is needed
6538 queries = Selector._splitQueries(selector, root);
6541 for (i = 0; (query = queries[i++]);) {
6542 result = fn(query[0], query[1], firstOnly);
6543 if (!firstOnly) { // coerce DOM Collection to Array
6544 result = Y.Array(result, 0, true);
6547 ret = ret.concat(result);
6551 if (queries.length > 1) { // remove dupes and sort by doc order
6552 ret = Selector._sort(Selector._deDupe(ret));
6556 return (firstOnly) ? (ret[0] || null) : ret;
6560 // allows element scoped queries to begin with combinator
6561 // e.g. query('> p', document.body) === query('body > p')
6562 _splitQueries: function(selector, node) {
6563 var groups = selector.split(','),
6569 // enforce for element scoping
6571 node.id = node.id || Y.guid();
6572 prefix = '[id="' + node.id + '"] ';
6575 for (i = 0, len = groups.length; i < len; ++i) {
6576 selector = prefix + groups[i];
6577 queries.push([selector, node]);
6584 _nativeQuery: function(selector, root, one) {
6585 if (Y.UA.webkit && selector.indexOf(':checked') > -1 &&
6586 (Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
6587 return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
6590 return root['querySelector' + (one ? '' : 'All')](selector);
6591 } catch(e) { // fallback to brute if available
6592 return Y.Selector.query(selector, root, one, true); // redo with skipNative true
6596 filter: function(nodes, selector) {
6600 if (nodes && selector) {
6601 for (i = 0; (node = nodes[i++]);) {
6602 if (Y.Selector.test(node, selector)) {
6603 ret[ret.length] = node;
6612 test: function(node, selector, root) {
6614 groups = selector.split(','),
6622 if (node && node.tagName) { // only test HTMLElements
6624 // we need a root if off-doc
6625 if (!root && !Y.DOM.inDoc(node)) {
6626 parent = node.parentNode;
6629 } else { // only use frag when no parent to query
6630 frag = node[OWNER_DOCUMENT].createDocumentFragment();
6631 frag.appendChild(node);
6636 root = root || node[OWNER_DOCUMENT];
6641 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
6642 group += '[id="' + node.id + '"]';
6643 items = Y.Selector.query(group, root);
6645 for (j = 0; item = items[j++];) {
6646 if (item === node) {
6656 if (useFrag) { // cleanup
6657 frag.removeChild(node);
6665 * A convenience function to emulate Y.Node's aNode.ancestor(selector).
6666 * @param {HTMLElement} element An HTMLElement to start the query from.
6667 * @param {String} selector The CSS selector to test the node against.
6668 * @return {HTMLElement} The ancestor node matching the selector, or null.
6669 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
6673 ancestor: function (element, selector, testSelf) {
6674 return Y.DOM.ancestor(element, function(n) {
6675 return Y.Selector.test(n, selector);
6680 Y.mix(Y.Selector, Selector, true);
6685 }, '3.3.0' ,{requires:['dom-base']});
6686 YUI.add('selector-css2', function(Y) {
6689 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
6691 * @submodule selector-css2
6696 * Provides helper methods for collecting and filtering DOM elements.
6699 var PARENT_NODE = 'parentNode',
6700 TAG_NAME = 'tagName',
6701 ATTRIBUTES = 'attributes',
6702 COMBINATOR = 'combinator',
6703 PSEUDOS = 'pseudos',
6705 Selector = Y.Selector,
6708 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
6710 _children: function(node, tag) {
6711 var ret = node.children,
6717 if (node.children && tag && node.children.tags) {
6718 children = node.children.tags(tag);
6719 } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
6720 childNodes = ret || node.childNodes;
6722 for (i = 0; (child = childNodes[i++]);) {
6723 if (child.tagName) {
6724 if (!tag || tag === child.tagName) {
6735 //attr: /(\[.*\])/g,
6736 attr: /(\[[^\]]*\])/g,
6737 pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
6741 * Mapping of shorthand tokens to corresponding attribute selector
6742 * @property shorthand
6746 '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
6747 '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
6751 * List of operators and corresponding boolean functions.
6752 * These functions are passed the attribute and the current node's value of the attribute.
6753 * @property operators
6757 '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
6759 //'=': '^{val}$', // equality
6760 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
6761 '|=': '^{val}-?' // optional hyphen-delimited
6765 'first-child': function(node) {
6766 return Y.Selector._children(node[PARENT_NODE])[0] === node;
6770 _bruteQuery: function(selector, root, firstOnly) {
6773 tokens = Selector._tokenize(selector),
6774 token = tokens[tokens.length - 1],
6775 rootDoc = Y.DOM._getDoc(root),
6782 // if we have an initial ID, set to root when in document
6784 if (tokens[0] && rootDoc === root &&
6785 (id = tokens[0].id) &&
6786 rootDoc.getElementById(id)) {
6787 root = rootDoc.getElementById(id);
6794 className = token.className;
6795 tagName = token.tagName || '*';
6797 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
6798 // try ID first, unless no root.all && root not in document
6799 // (root.all works off document, but not getElementById)
6800 // TODO: move to allById?
6801 if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) {
6802 nodes = Y.DOM.allById(id, root);
6804 } else if (className) {
6805 nodes = root.getElementsByClassName(className);
6806 } else { // default to tagName
6807 nodes = root.getElementsByTagName(tagName);
6810 } else { // brute getElementsByTagName('*')
6811 child = root.firstChild;
6813 if (child.tagName) { // only collect HTMLElements
6816 child = child.nextSilbing || child.firstChild;
6820 ret = Selector._filterNodes(nodes, tokens, firstOnly);
6827 _filterNodes: function(nodes, tokens, firstOnly) {
6830 len = tokens.length,
6835 getters = Y.Selector.getters,
6841 //FUNCTION = 'function',
6847 for (i = 0; (tmpNode = node = nodes[i++]);) {
6852 while (tmpNode && tmpNode.tagName) {
6854 tests = token.tests;
6857 while ((test = tests[--j])) {
6859 if (getters[test[0]]) {
6860 value = getters[test[0]](tmpNode, test[0]);
6862 value = tmpNode[test[0]];
6863 // use getAttribute for non-standard attributes
6864 if (value === undefined && tmpNode.getAttribute) {
6865 value = tmpNode.getAttribute(test[0]);
6869 if ((operator === '=' && value !== test[2]) || // fast path for equality
6870 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
6871 operator.test && !operator.test(value)) || // regex test
6872 (!operator.test && // protect against RegExp as function (webkit)
6873 typeof operator === 'function' && !operator(tmpNode, test[0]))) { // function test
6875 // skip non element nodes or non-matching tags
6876 if ((tmpNode = tmpNode[path])) {
6878 (!tmpNode.tagName ||
6879 (token.tagName && token.tagName !== tmpNode.tagName))
6881 tmpNode = tmpNode[path];
6889 n--; // move to next token
6890 // now that we've passed the test, move up the tree by combinator
6891 if (!pass && (combinator = token.combinator)) {
6892 path = combinator.axis;
6893 tmpNode = tmpNode[path];
6895 // skip non element nodes
6896 while (tmpNode && !tmpNode.tagName) {
6897 tmpNode = tmpNode[path];
6900 if (combinator.direct) { // one pass only
6904 } else { // success if we made it this far
6912 }// while (tmpNode = node = nodes[++i]);
6913 node = tmpNode = null;
6929 axis: 'previousSibling',
6937 re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
6938 fn: function(match, token) {
6939 var operator = match[2] || '',
6940 operators = Y.Selector.operators,
6943 // add prefiltering for ID and CLASS
6944 if ((match[1] === 'id' && operator === '=') ||
6945 (match[1] === 'className' &&
6946 Y.config.doc.documentElement.getElementsByClassName &&
6947 (operator === '~=' || operator === '='))) {
6948 token.prefilter = match[1];
6949 token[match[1]] = match[3];
6953 if (operator in operators) {
6954 test = operators[operator];
6955 if (typeof test === 'string') {
6956 match[3] = match[3].replace(Y.Selector._reRegExpTokens, '\\$1');
6957 test = Y.DOM._getRegExp(test.replace('{val}', match[3]));
6961 if (!token.last || token.prefilter !== match[1]) {
6962 return match.slice(1);
6969 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
6970 fn: function(match, token) {
6971 var tag = match[1].toUpperCase();
6972 token.tagName = tag;
6974 if (tag !== '*' && (!token.last || token.prefilter)) {
6975 return [TAG_NAME, '=', tag];
6977 if (!token.prefilter) {
6978 token.prefilter = 'tagName';
6984 re: /^\s*([>+~]|\s)\s*/,
6985 fn: function(match, token) {
6990 re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
6991 fn: function(match, token) {
6992 var test = Selector[PSEUDOS][match[1]];
6993 if (test) { // reorder match array
6994 return [match[2], test];
6995 } else { // selector token not supported (possibly missing CSS3 module)
7002 _getToken: function(token) {
7014 Break selector into token units per simple selector.
7015 Combinator is attached to the previous token.
7017 _tokenize: function(selector) {
7018 selector = selector || '';
7019 selector = Selector._replaceShorthand(Y.Lang.trim(selector));
7020 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
7021 query = selector, // original query for debug report
7022 tokens = [], // array of tokens
7023 found = false, // whether or not any matches were found this pass
7024 match, // the regex match
7029 Search for selector patterns, store, and strip them from the selector string
7030 until no patterns match (invalid selector) or we run out of chars.
7032 Multiple attributes and pseudos are allowed, in any order.
7034 'form:first-child[type=button]:not(button)[lang|=en]'
7038 found = false; // reset after full pass
7039 for (i = 0; (parser = Selector._parsers[i++]);) {
7040 if ( (match = parser.re.exec(selector)) ) { // note assignment
7041 if (parser.name !== COMBINATOR ) {
7042 token.selector = selector;
7044 selector = selector.replace(match[0], ''); // strip current match from selector
7045 if (!selector.length) {
7049 if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
7050 match[1] = Selector._attrFilters[match[1]];
7053 test = parser.fn(match, token);
7054 if (test === false) { // selector not supported
7058 token.tests.push(test);
7061 if (!selector.length || parser.name === COMBINATOR) {
7063 token = Selector._getToken(token);
7064 if (parser.name === COMBINATOR) {
7065 token.combinator = Y.Selector.combinators[match[1]];
7071 } while (found && selector.length);
7073 if (!found || selector.length) { // not fully parsed
7079 _replaceShorthand: function(selector) {
7080 var shorthand = Selector.shorthand,
7081 attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
7082 pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
7086 selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
7090 selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
7093 for (re in shorthand) {
7094 if (shorthand.hasOwnProperty(re)) {
7095 selector = selector.replace(Y.DOM._getRegExp(re, 'gi'), shorthand[re]);
7100 for (i = 0, len = attrs.length; i < len; ++i) {
7101 selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
7105 for (i = 0, len = pseudos.length; i < len; ++i) {
7106 selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
7113 'class': 'className',
7118 href: function(node, attr) {
7119 return Y.DOM.getAttribute(node, attr);
7124 Y.mix(Y.Selector, SelectorCSS2, true);
7125 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
7127 // IE wants class with native queries
7128 if (Y.Selector.useNative && Y.config.doc.querySelector) {
7129 Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
7134 }, '3.3.0' ,{requires:['selector-native']});
7137 YUI.add('selector', function(Y){}, '3.3.0' ,{use:['selector-native', 'selector-css2']});
7141 YUI.add('dom', function(Y){}, '3.3.0' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});
7143 YUI.add('event-custom-base', function(Y) {
7146 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
7148 * @module event-custom
7158 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
7160 * @module event-custom
7161 * @submodule event-custom-base
7165 * Allows for the insertion of methods that are executed before or after
7166 * a specified method
7177 * Cache of objects touched by the utility
7184 * Execute the supplied method before the specified function
7186 * @param fn {Function} the function to execute
7187 * @param obj the object hosting the method to displace
7188 * @param sFn {string} the name of the method to displace
7189 * @param c The execution context for fn
7190 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
7191 * when the event fires.
7192 * @return {string} handle for the subscription
7195 before: function(fn, obj, sFn, c) {
7198 a = [fn, c].concat(Y.Array(arguments, 4, true));
7199 f = Y.rbind.apply(Y, a);
7202 return this._inject(DO_BEFORE, f, obj, sFn);
7206 * Execute the supplied method after the specified function
7208 * @param fn {Function} the function to execute
7209 * @param obj the object hosting the method to displace
7210 * @param sFn {string} the name of the method to displace
7211 * @param c The execution context for fn
7212 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
7213 * @return {string} handle for the subscription
7216 after: function(fn, obj, sFn, c) {
7219 a = [fn, c].concat(Y.Array(arguments, 4, true));
7220 f = Y.rbind.apply(Y, a);
7223 return this._inject(DO_AFTER, f, obj, sFn);
7227 * Execute the supplied method after the specified function
7229 * @param when {string} before or after
7230 * @param fn {Function} the function to execute
7231 * @param obj the object hosting the method to displace
7232 * @param sFn {string} the name of the method to displace
7233 * @param c The execution context for fn
7234 * @return {string} handle for the subscription
7238 _inject: function(when, fn, obj, sFn) {
7241 var id = Y.stamp(obj), o, sid;
7243 if (! this.objs[id]) {
7244 // create a map entry for the obj if it doesn't exist
7251 // create a map entry for the method if it doesn't exist
7252 o[sFn] = new Y.Do.Method(obj, sFn);
7254 // re-route the method to our wrapper
7257 return o[sFn].exec.apply(o[sFn], arguments);
7262 sid = id + Y.stamp(fn) + sFn;
7264 // register the callback
7265 o[sFn].register(sid, fn, when);
7267 return new Y.EventHandle(o[sFn], sid);
7272 * Detach a before or after subscription
7274 * @param handle {string} the subscription handle
7276 detach: function(handle) {
7278 if (handle.detach) {
7284 _unload: function(e, me) {
7291 //////////////////////////////////////////////////////////////////////////
7294 * Contains the return value from the wrapped method, accessible
7295 * by 'after' event listeners.
7297 * @property Do.originalRetVal
7303 * Contains the current state of the return value, consumable by
7304 * 'after' event listeners, and updated if an after subscriber
7305 * changes the return value generated by the wrapped function.
7307 * @property Do.currentRetVal
7312 //////////////////////////////////////////////////////////////////////////
7315 * Wrapper for a displaced method with aop enabled
7318 * @param obj The object to operate on
7319 * @param sFn The name of the method to displace
7321 DO.Method = function(obj, sFn) {
7323 this.methodName = sFn;
7324 this.method = obj[sFn];
7330 * Register a aop subscriber
7332 * @param sid {string} the subscriber id
7333 * @param fn {Function} the function to execute
7334 * @param when {string} when to execute the function
7336 DO.Method.prototype.register = function (sid, fn, when) {
7338 this.after[sid] = fn;
7340 this.before[sid] = fn;
7345 * Unregister a aop subscriber
7347 * @param sid {string} the subscriber id
7348 * @param fn {Function} the function to execute
7349 * @param when {string} when to execute the function
7351 DO.Method.prototype._delete = function (sid) {
7352 delete this.before[sid];
7353 delete this.after[sid];
7357 * Execute the wrapped method
7360 DO.Method.prototype.exec = function () {
7362 var args = Y.Array(arguments, 0, true),
7370 if (bf.hasOwnProperty(i)) {
7371 ret = bf[i].apply(this.obj, args);
7373 switch (ret.constructor) {
7390 ret = this.method.apply(this.obj, args);
7393 DO.originalRetVal = ret;
7394 DO.currentRetVal = ret;
7396 // execute after methods.
7398 if (af.hasOwnProperty(i)) {
7399 newRet = af[i].apply(this.obj, args);
7400 // Stop processing if a Halt object is returned
7401 if (newRet && newRet.constructor == DO.Halt) {
7402 return newRet.retVal;
7403 // Check for a new return value
7404 } else if (newRet && newRet.constructor == DO.AlterReturn) {
7405 ret = newRet.newRetVal;
7406 // Update the static retval state
7407 DO.currentRetVal = ret;
7415 //////////////////////////////////////////////////////////////////////////
7418 * Return an AlterArgs object when you want to change the arguments that
7419 * were passed into the function. An example would be a service that scrubs
7420 * out illegal characters prior to executing the core business logic.
7421 * @class Do.AlterArgs
7423 DO.AlterArgs = function(msg, newArgs) {
7425 this.newArgs = newArgs;
7429 * Return an AlterReturn object when you want to change the result returned
7430 * from the core method to the caller
7431 * @class Do.AlterReturn
7433 DO.AlterReturn = function(msg, newRetVal) {
7435 this.newRetVal = newRetVal;
7439 * Return a Halt object when you want to terminate the execution
7440 * of all subsequent subscribers as well as the wrapped method
7441 * if it has not exectued yet.
7444 DO.Halt = function(msg, retVal) {
7446 this.retVal = retVal;
7450 * Return a Prevent object when you want to prevent the wrapped function
7451 * from executing, but want the remaining listeners to execute
7454 DO.Prevent = function(msg) {
7459 * Return an Error object when you want to terminate the execution
7460 * of all subsequent method calls.
7462 * @deprecated use Y.Do.Halt or Y.Do.Prevent
7467 //////////////////////////////////////////////////////////////////////////
7469 // Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
7473 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
7475 * @module event-custom
7476 * @submodule event-custom-base
7480 // var onsubscribeType = "_event:onsub",
7481 var AFTER = 'after',
7490 'defaultTargetOnly',
7506 YUI_LOG = 'yui:log';
7509 * Return value from all subscribe operations
7510 * @class EventHandle
7512 * @param {CustomEvent} evt the custom event.
7513 * @param {Subscriber} sub the subscriber.
7515 Y.EventHandle = function(evt, sub) {
7524 * The subscriber object
7530 Y.EventHandle.prototype = {
7531 batch: function(f, c) {
7532 f.call(c || this, this);
7533 if (Y.Lang.isArray(this.evt)) {
7534 Y.Array.each(this.evt, function(h) {
7535 h.batch.call(c || h, f);
7541 * Detaches this subscriber
7543 * @return {int} the number of detached listeners
7545 detach: function() {
7546 var evt = this.evt, detached = 0, i;
7548 if (Y.Lang.isArray(evt)) {
7549 for (i = 0; i < evt.length; i++) {
7550 detached += evt[i].detach();
7553 evt._delete(this.sub);
7563 * Monitor the event state for the subscribed event. The first parameter
7564 * is what should be monitored, the rest are the normal parameters when
7565 * subscribing to an event.
7567 * @param what {string} what to monitor ('attach', 'detach', 'publish').
7568 * @return {EventHandle} return value from the monitor event subscription.
7570 monitor: function(what) {
7571 return this.evt.monitor.apply(this.evt, arguments);
7576 * The CustomEvent class lets you define events for your application
7577 * that can be subscribed to by one or more independent component.
7579 * @param {String} type The type of event, which is passed to the callback
7580 * when the event fires.
7581 * @param {object} o configuration object.
7582 * @class CustomEvent
7585 Y.CustomEvent = function(type, o) {
7587 // if (arguments.length > 2) {
7588 // this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
7593 this.id = Y.stamp(this);
7596 * The type of event, returned to subscribers when the event fires
7603 * The context the the event will fire from by default. Defaults to the YUI
7611 * Monitor when an event is attached or detached.
7613 * @property monitored
7616 // this.monitored = false;
7618 this.logSystem = (type == YUI_LOG);
7621 * If 0, this event does not broadcast. If 1, the YUI instance is notified
7622 * every time this event fires. If 2, the YUI instance and the YUI global
7623 * (if event is enabled on the global) are notified every time this event
7625 * @property broadcast
7628 // this.broadcast = 0;
7631 * By default all custom events are logged in the debug build, set silent
7632 * to true to disable debug outpu for this event.
7636 this.silent = this.logSystem;
7639 * Specifies whether this event should be queued when the host is actively
7640 * processing an event. This will effect exectution order of the callbacks
7641 * for the various events.
7642 * @property queuable
7646 // this.queuable = false;
7649 * The subscribers to this event
7650 * @property subscribers
7651 * @type Subscriber {}
7653 this.subscribers = {};
7656 * 'After' subscribers
7658 * @type Subscriber {}
7663 * This event has fired if true
7669 // this.fired = false;
7672 * An array containing the arguments the custom event
7673 * was last fired with.
7674 * @property firedWith
7680 * This event should only fire one time if true, and if
7681 * it has fired, any new subscribers should be notified
7684 * @property fireOnce
7688 // this.fireOnce = false;
7691 * fireOnce listeners will fire syncronously unless async
7697 //this.async = false;
7700 * Flag for stopPropagation that is modified during fire()
7701 * 1 means to stop propagation to bubble targets. 2 means
7702 * to also stop additional subscribers on this target.
7706 // this.stopped = 0;
7709 * Flag for preventDefault that is modified during fire().
7710 * if it is not 0, the default behavior for this event
7711 * @property prevented
7714 // this.prevented = 0;
7717 * Specifies the host for this custom event. This is used
7718 * to enable event bubbling
7722 // this.host = null;
7725 * The default function to execute after event listeners
7726 * have fire, but only if the default action was not
7728 * @property defaultFn
7731 // this.defaultFn = null;
7734 * The function to execute if a subscriber calls
7735 * stopPropagation or stopImmediatePropagation
7736 * @property stoppedFn
7739 // this.stoppedFn = null;
7742 * The function to execute if a subscriber calls
7744 * @property preventedFn
7747 // this.preventedFn = null;
7750 * Specifies whether or not this event's default function
7751 * can be cancelled by a subscriber by executing preventDefault()
7752 * on the event facade
7753 * @property preventable
7757 this.preventable = true;
7760 * Specifies whether or not a subscriber can stop the event propagation
7761 * via stopPropagation(), stopImmediatePropagation(), or halt()
7763 * Events can only bubble if emitFacade is true.
7769 this.bubbles = true;
7772 * Supports multiple options for listener signatures in order to
7774 * @property signature
7778 this.signature = YUI3_SIGNATURE;
7781 this.afterCount = 0;
7783 // this.hasSubscribers = false;
7785 // this.hasAfters = false;
7788 * If set to true, the custom event will deliver an EventFacade object
7789 * that is similar to a DOM event object.
7790 * @property emitFacade
7794 // this.emitFacade = false;
7796 this.applyConfig(o, true);
7798 // this.log("Creating " + this.type);
7802 Y.CustomEvent.prototype = {
7804 hasSubs: function(when) {
7805 var s = this.subCount, a = this.afterCount, sib = this.sibling;
7809 a += sib.afterCount;
7813 return (when == 'after') ? a : s;
7820 * Monitor the event state for the subscribed event. The first parameter
7821 * is what should be monitored, the rest are the normal parameters when
7822 * subscribing to an event.
7824 * @param what {string} what to monitor ('detach', 'attach', 'publish').
7825 * @return {EventHandle} return value from the monitor event subscription.
7827 monitor: function(what) {
7828 this.monitored = true;
7829 var type = this.id + '|' + this.type + '_' + what,
7830 args = Y.Array(arguments, 0, true);
7832 return this.host.on.apply(this.host, args);
7836 * Get all of the subscribers to this event and any sibling event
7838 * @return {Array} first item is the on subscribers, second the after.
7840 getSubs: function() {
7841 var s = Y.merge(this.subscribers), a = Y.merge(this.afters), sib = this.sibling;
7844 Y.mix(s, sib.subscribers);
7845 Y.mix(a, sib.afters);
7852 * Apply configuration properties. Only applies the CONFIG whitelist
7853 * @method applyConfig
7854 * @param o hash of properties to apply.
7855 * @param force {boolean} if true, properties that exist on the event
7856 * will be overwritten.
7858 applyConfig: function(o, force) {
7860 Y.mix(this, o, force, CONFIGS);
7864 _on: function(fn, context, args, when) {
7867 this.log('Invalid callback for CE: ' + this.type);
7870 var s = new Y.Subscriber(fn, context, args, when);
7872 if (this.fireOnce && this.fired) {
7874 setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0);
7876 this._notify(s, this.firedWith);
7880 if (when == AFTER) {
7881 this.afters[s.id] = s;
7884 this.subscribers[s.id] = s;
7888 return new Y.EventHandle(this, s);
7893 * Listen for this event
7895 * @param {Function} fn The function to execute.
7896 * @return {EventHandle} Unsubscribe handle.
7897 * @deprecated use on.
7899 subscribe: function(fn, context) {
7900 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
7901 return this._on(fn, context, a, true);
7905 * Listen for this event
7907 * @param {Function} fn The function to execute.
7908 * @param {object} context optional execution context.
7909 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
7910 * when the event fires.
7911 * @return {EventHandle} An object with a detach method to detch the handler(s).
7913 on: function(fn, context) {
7914 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
7916 this.host._monitor('attach', this.type, {
7920 return this._on(fn, context, a, true);
7924 * Listen for this event after the normal subscribers have been notified and
7925 * the default behavior has been applied. If a normal subscriber prevents the
7926 * default behavior, it also prevents after listeners from firing.
7928 * @param {Function} fn The function to execute.
7929 * @param {object} context optional execution context.
7930 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
7931 * when the event fires.
7932 * @return {EventHandle} handle Unsubscribe handle.
7934 after: function(fn, context) {
7935 var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
7936 return this._on(fn, context, a, AFTER);
7942 * @param {Function} fn The subscribed function to remove, if not supplied
7943 * all will be removed.
7944 * @param {Object} context The context object passed to subscribe.
7945 * @return {int} returns the number of subscribers unsubscribed.
7947 detach: function(fn, context) {
7948 // unsubscribe handle
7949 if (fn && fn.detach) {
7955 subs = Y.merge(this.subscribers, this.afters);
7958 if (subs.hasOwnProperty(i)) {
7960 if (s && (!fn || fn === s.fn)) {
7972 * @method unsubscribe
7973 * @param {Function} fn The subscribed function to remove, if not supplied
7974 * all will be removed.
7975 * @param {Object} context The context object passed to subscribe.
7976 * @return {int|undefined} returns the number of subscribers unsubscribed.
7977 * @deprecated use detach.
7979 unsubscribe: function() {
7980 return this.detach.apply(this, arguments);
7984 * Notify a single subscriber
7986 * @param {Subscriber} s the subscriber.
7987 * @param {Array} args the arguments array to apply to the listener.
7990 _notify: function(s, args, ef) {
7992 this.log(this.type + '->' + 'sub: ' + s.id);
7996 ret = s.notify(args, this);
7998 if (false === ret || this.stopped > 1) {
7999 this.log(this.type + ' cancelled by subscriber');
8007 * Logger abstraction to centralize the application of the silent flag
8009 * @param {string} msg message to log.
8010 * @param {string} cat log category.
8012 log: function(msg, cat) {
8018 * Notifies the subscribers. The callback functions will be executed
8019 * from the context specified when the event was created, and with the
8020 * following parameters:
8022 * <li>The type of event</li>
8023 * <li>All of the arguments fire() was executed with as an array</li>
8024 * <li>The custom object (if any) that was passed into the subscribe()
8028 * @param {Object*} arguments an arbitrary set of parameters to pass to
8030 * @return {boolean} false if one of the subscribers returned false,
8035 if (this.fireOnce && this.fired) {
8036 this.log('fireOnce event: ' + this.type + ' already fired');
8040 var args = Y.Array(arguments, 0, true);
8042 // this doesn't happen if the event isn't published
8043 // this.host._monitor('fire', this.type, args);
8046 this.firedWith = args;
8048 if (this.emitFacade) {
8049 return this.fireComplex(args);
8051 return this.fireSimple(args);
8056 fireSimple: function(args) {
8059 if (this.hasSubs()) {
8060 // this._procSubs(Y.merge(this.subscribers, this.afters), args);
8061 var subs = this.getSubs();
8062 this._procSubs(subs[0], args);
8063 this._procSubs(subs[1], args);
8065 this._broadcast(args);
8066 return this.stopped ? false : true;
8069 // Requires the event-custom-complex module for full funcitonality.
8070 fireComplex: function(args) {
8071 args[0] = args[0] || {};
8072 return this.fireSimple(args);
8075 _procSubs: function(subs, args, ef) {
8078 if (subs.hasOwnProperty(i)) {
8081 if (false === this._notify(s, args, ef)) {
8084 if (this.stopped == 2) {
8094 _broadcast: function(args) {
8095 if (!this.stopped && this.broadcast) {
8097 var a = Y.Array(args);
8098 a.unshift(this.type);
8100 if (this.host !== Y) {
8104 if (this.broadcast == 2) {
8105 Y.Global.fire.apply(Y.Global, a);
8111 * Removes all listeners
8112 * @method unsubscribeAll
8113 * @return {int} The number of listeners unsubscribed.
8114 * @deprecated use detachAll.
8116 unsubscribeAll: function() {
8117 return this.detachAll.apply(this, arguments);
8121 * Removes all listeners
8123 * @return {int} The number of listeners unsubscribed.
8125 detachAll: function() {
8126 return this.detach();
8131 * @param subscriber object.
8134 _delete: function(s) {
8136 if (this.subscribers[s.id]) {
8137 delete this.subscribers[s.id];
8140 if (this.afters[s.id]) {
8141 delete this.afters[s.id];
8147 this.host._monitor('detach', this.type, {
8155 // delete s.context;
8161 /////////////////////////////////////////////////////////////////////
8164 * Stores the subscriber information to be used when the event fires.
8165 * @param {Function} fn The wrapped function to execute.
8166 * @param {Object} context The value of the keyword 'this' in the listener.
8167 * @param {Array} args* 0..n additional arguments to supply the listener.
8172 Y.Subscriber = function(fn, context, args) {
8175 * The callback that will be execute when the event fires
8176 * This is wrapped by Y.rbind if obj was supplied.
8183 * Optional 'this' keyword for the listener
8187 this.context = context;
8190 * Unique subscriber id
8194 this.id = Y.stamp(this);
8197 * Additional arguments to propagate to the subscriber
8204 * Custom events for a given fire transaction.
8206 * @type {EventTarget}
8208 // this.events = null;
8211 * This listener only reacts to the event once
8214 // this.once = false;
8218 Y.Subscriber.prototype = {
8220 _notify: function(c, args, ce) {
8221 if (this.deleted && !this.postponed) {
8222 if (this.postponed) {
8224 delete this.context;
8226 delete this.postponed;
8230 var a = this.args, ret;
8231 switch (ce.signature) {
8233 ret = this.fn.call(c, ce.type, args, c);
8236 ret = this.fn.call(c, args[0] || null, c);
8241 a = (a) ? args.concat(a) : args;
8242 ret = this.fn.apply(c, a);
8244 ret = this.fn.call(c);
8256 * Executes the subscriber.
8258 * @param args {Array} Arguments array for the subscriber.
8259 * @param ce {CustomEvent} The custom event that sent the notification.
8261 notify: function(args, ce) {
8262 var c = this.context,
8266 c = (ce.contextFn) ? ce.contextFn() : ce.context;
8269 // only catch errors if we will not re-throw them.
8270 if (Y.config.throwFail) {
8271 ret = this._notify(c, args, ce);
8274 ret = this._notify(c, args, ce);
8276 Y.error(this + ' failed: ' + e.message, e);
8284 * Returns true if the fn and obj match this objects properties.
8285 * Used by the unsubscribe method to match the right subscriber.
8288 * @param {Function} fn the function to execute.
8289 * @param {Object} context optional 'this' keyword for the listener.
8290 * @return {boolean} true if the supplied arguments match this
8291 * subscriber's signature.
8293 contains: function(fn, context) {
8295 return ((this.fn == fn) && this.context == context);
8297 return (this.fn == fn);
8304 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
8306 * @module event-custom
8307 * @submodule event-custom-base
8311 * EventTarget provides the implementation for any object to
8312 * publish, subscribe and fire to custom events, and also
8313 * alows other EventTargets to target the object with events
8314 * sourced from the other object.
8315 * EventTarget is designed to be used with Y.augment to wrap
8316 * EventCustom in an interface that allows events to be listened to
8317 * and fired by name. This makes it possible for implementing code to
8318 * subscribe to an event that either has not been created yet, or will
8319 * not be created at all.
8320 * @class EventTarget
8321 * @param opts a configuration object
8322 * @config emitFacade {boolean} if true, all events will emit event
8323 * facade payloads by default (default false)
8324 * @config prefix {string} the prefix to apply to non-prefixed event names
8325 * @config chain {boolean} if true, on/after/detach return the host to allow
8326 * chaining, otherwise they return an EventHandle (default false)
8330 PREFIX_DELIMITER = ':',
8331 CATEGORY_DELIMITER = '|',
8332 AFTER_PREFIX = '~AFTER~',
8335 _wildType = Y.cached(function(type) {
8336 return type.replace(/(.*)(:)(.*)/, "*$2$3");
8340 * If the instance has a prefix attribute and the
8341 * event type is not prefixed, the instance prefix is
8342 * applied to the supplied type.
8346 _getType = Y.cached(function(type, pre) {
8348 if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
8352 return pre + PREFIX_DELIMITER + type;
8356 * Returns an array with the detach key (if provided),
8357 * and the prefixed event name from _getType
8358 * Y.on('detachcategory| menu:click', fn)
8359 * @method _parseType
8362 _parseType = Y.cached(function(type, pre) {
8364 var t = type, detachcategory, after, i;
8366 if (!L.isString(t)) {
8370 i = t.indexOf(AFTER_PREFIX);
8374 t = t.substr(AFTER_PREFIX.length);
8377 i = t.indexOf(CATEGORY_DELIMITER);
8380 detachcategory = t.substr(0, (i));
8387 // detach category, full type with instance prefix, is this an after listener, short type
8388 return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
8391 ET = function(opts) {
8394 var o = (L.isObject(opts)) ? opts : {};
8396 this._yuievt = this._yuievt || {
8406 chain: ('chain' in o) ? o.chain : Y.config.chain,
8411 context: o.context || this,
8413 emitFacade: o.emitFacade,
8414 fireOnce: o.fireOnce,
8415 queuable: o.queuable,
8416 monitored: o.monitored,
8417 broadcast: o.broadcast,
8418 defaultTargetOnly: o.defaultTargetOnly,
8419 bubbles: ('bubbles' in o) ? o.bubbles : true
8429 * Listen to a custom event hosted by this object one time.
8430 * This is the equivalent to <code>on</code> except the
8431 * listener is immediatelly detached when it is executed.
8433 * @param type {string} The type of the event
8434 * @param fn {Function} The callback
8435 * @param context {object} optional execution context.
8436 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
8437 * @return the event target or a detach handle per 'chain' config
8440 var handle = this.on.apply(this, arguments);
8441 handle.batch(function(hand) {
8443 hand.sub.once = true;
8450 * Takes the type parameter passed to 'on' and parses out the
8451 * various pieces that could be included in the type. If the
8452 * event type is passed without a prefix, it will be expanded
8453 * to include the prefix one is supplied or the event target
8454 * is configured with a default prefix.
8456 * @param {string} type the type
8457 * @param {string} [pre=this._yuievt.config.prefix] the prefix
8459 * @return {Array} an array containing:
8460 * * the detach category, if supplied,
8461 * * the prefixed event type,
8462 * * whether or not this is an after listener,
8463 * * the supplied event type
8465 parseType: function(type, pre) {
8466 return _parseType(type, pre || this._yuievt.config.prefix);
8470 * Subscribe to a custom event hosted by this object
8472 * @param type {string} The type of the event
8473 * @param fn {Function} The callback
8474 * @param context {object} optional execution context.
8475 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
8476 * @return the event target or a detach handle per 'chain' config
8478 on: function(type, fn, context) {
8480 var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
8481 detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
8482 Node = Y.Node, n, domevent, isArr;
8484 // full name, args, detachcategory, after
8485 this._monitor('attach', parts[1], {
8491 if (L.isObject(type)) {
8493 if (L.isFunction(type)) {
8494 return Y.Do.before.apply(Y.Do, arguments);
8499 args = YArray(arguments, 0, true);
8502 if (L.isArray(type)) {
8506 after = type._after;
8509 Y.each(type, function(v, k) {
8511 if (L.isObject(v)) {
8512 f = v.fn || ((L.isFunction(v)) ? v : f);
8516 var nv = (after) ? AFTER_PREFIX : '';
8518 args[0] = nv + ((isArr) ? v : k);
8522 ret.push(this.on.apply(this, args));
8526 return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
8530 detachcategory = parts[0];
8532 shorttype = parts[3];
8534 // extra redirection so we catch adaptor events too. take a look at this.
8535 if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
8536 args = YArray(arguments, 0, true);
8537 args.splice(2, 0, Node.getDOMNode(this));
8538 return Y.on.apply(Y, args);
8543 if (Y.instanceOf(this, YUI)) {
8545 adapt = Y.Env.evt.plugins[type];
8546 args = YArray(arguments, 0, true);
8547 args[0] = shorttype;
8552 if (Y.instanceOf(n, Y.NodeList)) {
8553 n = Y.NodeList.getDOMNodes(n);
8554 } else if (Y.instanceOf(n, Node)) {
8555 n = Node.getDOMNode(n);
8558 domevent = (shorttype in Node.DOM_EVENTS);
8560 // Captures both DOM events and event plugins.
8566 // check for the existance of an event adaptor
8568 handle = adapt.on.apply(Y, args);
8569 } else if ((!type) || domevent) {
8570 handle = Y.Event._attach(args);
8576 ce = this._yuievt.events[type] || this.publish(type);
8577 handle = ce._on(fn, context, (arguments.length > 3) ? YArray(arguments, 3, true) : null, (after) ? 'after' : true);
8580 if (detachcategory) {
8581 store[detachcategory] = store[detachcategory] || {};
8582 store[detachcategory][type] = store[detachcategory][type] || [];
8583 store[detachcategory][type].push(handle);
8586 return (this._yuievt.chain) ? this : handle;
8591 * subscribe to an event
8593 * @deprecated use on
8595 subscribe: function() {
8596 return this.on.apply(this, arguments);
8600 * Detach one or more listeners the from the specified event
8602 * @param type {string|Object} Either the handle to the subscriber or the
8603 * type of event. If the type
8604 * is not specified, it will attempt to remove
8605 * the listener from all hosted events.
8606 * @param fn {Function} The subscribed function to unsubscribe, if not
8607 * supplied, all subscribers will be removed.
8608 * @param context {Object} The custom object passed to subscribe. This is
8609 * optional, but if supplied will be used to
8610 * disambiguate multiple listeners that are the same
8611 * (e.g., you subscribe many object using a function
8612 * that lives on the prototype)
8613 * @return {EventTarget} the host
8615 detach: function(type, fn, context) {
8616 var evts = this._yuievt.events, i,
8617 Node = Y.Node, isNode = Node && (Y.instanceOf(this, Node));
8619 // detachAll disabled on the Y instance.
8620 if (!type && (this !== Y)) {
8622 if (evts.hasOwnProperty(i)) {
8623 evts[i].detach(fn, context);
8627 Y.Event.purgeElement(Node.getDOMNode(this));
8633 var parts = _parseType(type, this._yuievt.config.prefix),
8634 detachcategory = L.isArray(parts) ? parts[0] : null,
8635 shorttype = (parts) ? parts[3] : null,
8636 adapt, store = Y.Env.evt.handles, detachhost, cat, args,
8639 keyDetacher = function(lcat, ltype, host) {
8640 var handles = lcat[ltype], ce, i;
8642 for (i = handles.length - 1; i >= 0; --i) {
8643 ce = handles[i].evt;
8644 if (ce.host === host || ce.el === host) {
8645 handles[i].detach();
8651 if (detachcategory) {
8653 cat = store[detachcategory];
8655 detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
8659 keyDetacher(cat, type, detachhost);
8662 if (cat.hasOwnProperty(i)) {
8663 keyDetacher(cat, i, detachhost);
8671 // If this is an event handle, use it to detach
8672 } else if (L.isObject(type) && type.detach) {
8675 // extra redirection so we catch adaptor events too. take a look at this.
8676 } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
8677 args = YArray(arguments, 0, true);
8678 args[2] = Node.getDOMNode(this);
8679 Y.detach.apply(Y, args);
8683 adapt = Y.Env.evt.plugins[shorttype];
8685 // The YUI instance handles DOM events and adaptors
8686 if (Y.instanceOf(this, YUI)) {
8687 args = YArray(arguments, 0, true);
8688 // use the adaptor specific detach code if
8689 if (adapt && adapt.detach) {
8690 adapt.detach.apply(Y, args);
8693 } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
8695 Y.Event.detach.apply(Y.Event, args);
8701 ce = evts[parts[1]];
8703 ce.detach(fn, context);
8711 * @method unsubscribe
8712 * @deprecated use detach
8714 unsubscribe: function() {
8715 return this.detach.apply(this, arguments);
8719 * Removes all listeners from the specified event. If the event type
8720 * is not specified, all listeners from all hosted custom events will
8723 * @param type {string} The type, or name of the event
8725 detachAll: function(type) {
8726 return this.detach(type);
8730 * Removes all listeners from the specified event. If the event type
8731 * is not specified, all listeners from all hosted custom events will
8733 * @method unsubscribeAll
8734 * @param type {string} The type, or name of the event
8735 * @deprecated use detachAll
8737 unsubscribeAll: function() {
8738 return this.detachAll.apply(this, arguments);
8742 * Creates a new custom event of the specified type. If a custom event
8743 * by that name already exists, it will not be re-created. In either
8744 * case the custom event is returned.
8748 * @param type {string} the type, or name of the event
8749 * @param opts {object} optional config params. Valid properties are:
8753 * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
8756 * 'bubbles': whether or not this event bubbles (true)
8757 * Events can only bubble if emitFacade is true.
8760 * 'context': the default execution context for the listeners (this)
8763 * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
8766 * 'emitFacade': whether or not this event emits a facade (false)
8769 * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
8772 * 'fireOnce': if an event is configured to fire once, new subscribers after
8773 * the fire will be notified immediately.
8776 * 'async': fireOnce event listeners will fire synchronously if the event has already
8777 * fired unless async is true.
8780 * 'preventable': whether or not preventDefault() has an effect (true)
8783 * 'preventedFn': a function that is executed when preventDefault is called
8786 * 'queuable': whether or not this event can be queued during bubbling (false)
8789 * 'silent': if silent is true, debug messages are not provided for this event.
8792 * 'stoppedFn': a function that is executed when stopPropagation is called
8796 * 'monitored': specifies whether or not this event should send notifications about
8797 * when the event has been attached, detached, or published.
8800 * 'type': the event type (valid option if not provided as the first parameter to publish)
8804 * @return {CustomEvent} the custom event
8807 publish: function(type, opts) {
8808 var events, ce, ret, defaults,
8809 edata = this._yuievt,
8810 pre = edata.config.prefix;
8812 type = (pre) ? _getType(type, pre) : type;
8814 this._monitor('publish', type, {
8818 if (L.isObject(type)) {
8820 Y.each(type, function(v, k) {
8821 ret[k] = this.publish(k, v || opts);
8827 events = edata.events;
8831 // ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
8833 ce.applyConfig(opts, true);
8837 defaults = edata.defaults;
8840 ce = new Y.CustomEvent(type,
8841 (opts) ? Y.merge(defaults, opts) : defaults);
8845 // make sure we turn the broadcast flag off if this
8846 // event was published as a result of bubbling
8847 // if (opts instanceof Y.CustomEvent) {
8848 // events[type].broadcast = false;
8851 return events[type];
8855 * This is the entry point for the event monitoring system.
8856 * You can monitor 'attach', 'detach', 'fire', and 'publish'.
8857 * When configured, these events generate an event. click ->
8858 * click_attach, click_detach, click_publish -- these can
8859 * be subscribed to like other events to monitor the event
8860 * system. Inividual published events can have monitoring
8861 * turned on or off (publish can't be turned off before it
8862 * it published) by setting the events 'monitor' config.
8866 _monitor: function(what, type, o) {
8867 var monitorevt, ce = this.getEvent(type);
8868 if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
8869 monitorevt = type + '_' + what;
8871 this.fire.call(this, monitorevt, o);
8876 * Fire a custom event by name. The callback functions will be executed
8877 * from the context specified when the event was created, and with the
8878 * following parameters.
8880 * If the custom event object hasn't been created, then the event hasn't
8881 * been published and it has no subscribers. For performance sake, we
8882 * immediate exit in this case. This means the event won't bubble, so
8883 * if the intention is that a bubble target be notified, the event must
8884 * be published on this object first.
8886 * The first argument is the event type, and any additional arguments are
8887 * passed to the listeners as parameters. If the first of these is an
8888 * object literal, and the event is configured to emit an event facade,
8889 * that object is mixed into the event facade and the facade is provided
8890 * in place of the original object.
8893 * @param type {String|Object} The type of the event, or an object that contains
8894 * a 'type' property.
8895 * @param arguments {Object*} an arbitrary set of parameters to pass to
8896 * the handler. If the first of these is an object literal and the event is
8897 * configured to emit an event facade, the event facade will replace that
8898 * parameter after the properties the object literal contains are copied to
8900 * @return {EventTarget} the event host
8903 fire: function(type) {
8905 var typeIncluded = L.isString(type),
8906 t = (typeIncluded) ? type : (type && type.type),
8907 ce, ret, pre = this._yuievt.config.prefix, ce2,
8908 args = (typeIncluded) ? YArray(arguments, 1, true) : arguments;
8910 t = (pre) ? _getType(t, pre) : t;
8912 this._monitor('fire', t, {
8916 ce = this.getEvent(t, true);
8917 ce2 = this.getSibling(t, ce);
8920 ce = this.publish(t);
8923 // this event has not been published or subscribed to
8925 if (this._yuievt.hasTargets) {
8926 return this.bubble({ type: t }, args, this);
8929 // otherwise there is nothing to be done
8933 ret = ce.fire.apply(ce, args);
8936 return (this._yuievt.chain) ? this : ret;
8939 getSibling: function(type, ce) {
8941 // delegate to *:type events if there are subscribers
8942 if (type.indexOf(PREFIX_DELIMITER) > -1) {
8943 type = _wildType(type);
8944 // console.log(type);
8945 ce2 = this.getEvent(type, true);
8947 // console.log("GOT ONE: " + type);
8948 ce2.applyConfig(ce);
8949 ce2.bubbles = false;
8951 // ret = ce2.fire.apply(ce2, a);
8959 * Returns the custom event of the provided type has been created, a
8960 * falsy value otherwise
8962 * @param type {string} the type, or name of the event
8963 * @param prefixed {string} if true, the type is prefixed already
8964 * @return {CustomEvent} the custom event or null
8966 getEvent: function(type, prefixed) {
8969 pre = this._yuievt.config.prefix;
8970 type = (pre) ? _getType(type, pre) : type;
8972 e = this._yuievt.events;
8973 return e[type] || null;
8977 * Subscribe to a custom event hosted by this object. The
8978 * supplied callback will execute after any listeners add
8979 * via the subscribe method, and after the default function,
8980 * if configured for the event, has executed.
8982 * @param type {string} The type of the event
8983 * @param fn {Function} The callback
8984 * @param context {object} optional execution context.
8985 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
8986 * @return the event target or a detach handle per 'chain' config
8988 after: function(type, fn) {
8990 var a = YArray(arguments, 0, true);
8992 switch (L.type(type)) {
8994 return Y.Do.after.apply(Y.Do, arguments);
8996 // YArray.each(a[0], function(v) {
8997 // v = AFTER_PREFIX + v;
9004 a[0] = AFTER_PREFIX + type;
9007 return this.on.apply(this, a);
9012 * Executes the callback before a DOM event, custom event
9013 * or method. If the first argument is a function, it
9014 * is assumed the target is a method. For DOM and custom
9015 * events, this is an alias for Y.on.
9017 * For DOM and custom events:
9018 * type, callback, context, 0-n arguments
9021 * callback, object (method host), methodName, context, 0-n arguments
9024 * @return detach handle
9026 before: function() {
9027 return this.on.apply(this, arguments);
9034 // make Y an event target
9035 Y.mix(Y, ET.prototype, false, false, {
9041 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
9044 * Hosts YUI page level events. This is where events bubble to
9045 * when the broadcast config is set to 2. This property is
9046 * only available if the custom event module is loaded.
9051 Y.Global = YUI.Env.globalEvents;
9053 // @TODO implement a global namespace function on Y.Global?
9056 * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
9057 * most events exposed by YUI. This includes custom events, DOM events, and
9058 * function events. <code>detach</code> is also provided to remove listeners
9059 * serviced by this function.
9061 * The signature that <code>on</code> accepts varies depending on the type
9062 * of event being consumed. Refer to the specific methods that will
9063 * service a specific request for additional information about subscribing
9064 * to that type of event.
9067 * <li>Custom events. These events are defined by various
9068 * modules in the library. This type of event is delegated to
9069 * <code>EventTarget</code>'s <code>on</code> method.
9071 * <li>The type of the event</li>
9072 * <li>The callback to execute</li>
9073 * <li>An optional context object</li>
9074 * <li>0..n additional arguments to supply the callback.</li>
9077 * <code>Y.on('drag:drophit', function() { // start work });</code>
9079 * <li>DOM events. These are moments reported by the browser related
9080 * to browser functionality and user interaction.
9081 * This type of event is delegated to <code>Event</code>'s
9082 * <code>attach</code> method.
9084 * <li>The type of the event</li>
9085 * <li>The callback to execute</li>
9086 * <li>The specification for the Node(s) to attach the listener
9087 * to. This can be a selector, collections, or Node/Element
9089 * <li>An optional context object</li>
9090 * <li>0..n additional arguments to supply the callback.</li>
9093 * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
9095 * <li>Function events. These events can be used to react before or after a
9096 * function is executed. This type of event is delegated to <code>Event.Do</code>'s
9097 * <code>before</code> method.
9099 * <li>The callback to execute</li>
9100 * <li>The object that has the function that will be listened for.</li>
9101 * <li>The name of the function to listen for.</li>
9102 * <li>An optional context object</li>
9103 * <li>0..n additional arguments to supply the callback.</li>
9105 * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
9109 * <code>on</code> corresponds to the moment before any default behavior of
9110 * the event. <code>after</code> works the same way, but these listeners
9111 * execute after the event's default behavior. <code>before</code> is an
9112 * alias for <code>on</code>.
9115 * @param type event type (this parameter does not apply for function events)
9116 * @param fn the callback
9117 * @param context optionally change the value of 'this' in the callback
9118 * @param args* 0..n additional arguments to pass to the callback.
9119 * @return the event target or a detach handle per 'chain' config
9124 * Listen for an event one time. Equivalent to <code>on</code>, except that
9125 * the listener is immediately detached when executed.
9128 * @param type event type (this parameter does not apply for function events)
9129 * @param fn the callback
9130 * @param context optionally change the value of 'this' in the callback
9131 * @param args* 0..n additional arguments to pass to the callback.
9132 * @return the event target or a detach handle per 'chain' config
9137 * after() is a unified interface for subscribing to
9138 * most events exposed by YUI. This includes custom events,
9139 * DOM events, and AOP events. This works the same way as
9140 * the on() function, only it operates after any default
9141 * behavior for the event has executed. @see <code>on</code> for more
9144 * @param type event type (this parameter does not apply for function events)
9145 * @param fn the callback
9146 * @param context optionally change the value of 'this' in the callback
9147 * @param args* 0..n additional arguments to pass to the callback.
9148 * @return the event target or a detach handle per 'chain' config
9153 }, '3.3.0' ,{requires:['oop']});
9154 var GLOBAL_ENV = YUI.Env;
9156 if (!GLOBAL_ENV._ready) {
9157 GLOBAL_ENV._ready = function() {
9158 GLOBAL_ENV.DOMReady = true;
9159 GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
9162 // if (!YUI.UA.ie) {
9163 GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
9167 YUI.add('event-base', function(Y) {
9170 * DOM event listener abstraction layer
9172 * @submodule event-base
9176 * The domready event fires at the moment the browser's DOM is
9177 * usable. In most cases, this is before images are fully
9178 * downloaded, allowing you to provide a more responsive user
9181 * In YUI 3, domready subscribers will be notified immediately if
9182 * that moment has already passed when the subscription is created.
9184 * One exception is if the yui.js file is dynamically injected into
9185 * the page. If this is done, you must tell the YUI instance that
9186 * you did this in order for DOMReady (and window load events) to
9187 * fire normally. That configuration option is 'injected' -- set
9188 * it to true if the yui.js script is not included inline.
9190 * This method is part of the 'event-ready' module, which is a
9191 * submodule of 'event'.
9196 Y.publish('domready', {
9201 if (GLOBAL_ENV.DOMReady) {
9204 Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready');
9208 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
9211 * @submodule event-base
9215 * Wraps a DOM event, properties requiring browser abstraction are
9216 * fixed here. Provids a security layer when required.
9217 * @class DOMEventFacade
9218 * @param ev {Event} the DOM event
9219 * @param currentTarget {HTMLElement} the element the listener was attached to
9220 * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
9228 * webkit key remapping required for Safari < 3.1
9229 * @property webkitKeymap
9237 63276: 33, // page up
9238 63277: 34, // page down
9239 25: 9, // SHIFT-TAB (Safari provides a different key code in
9240 // this case, even though the shiftKey modifier is set)
9241 63272: 46, // delete
9247 * Returns a wrapped node. Intended to be used on event targets,
9248 * so it will return the node's parent if the target is a text
9251 * If accessing a property of the node throws an error, this is
9252 * probably the anonymous div wrapper Gecko adds inside text
9253 * nodes. This likely will only occur when attempting to access
9254 * the relatedTarget. In this case, we now return null because
9255 * the anonymous div is completely useless and we do not know
9256 * what the related target was because we can't even get to
9257 * the element's parent node.
9262 resolve = function(n) {
9267 if (n && 3 == n.nodeType) {
9277 DOMEventFacade = function(ev, currentTarget, wrapper) {
9279 this._currentTarget = currentTarget;
9280 this._wrapper = wrapper || EMPTY;
9286 Y.extend(DOMEventFacade, Object, {
9290 var e = this._event,
9291 overrides = this._wrapper.overrides,
9295 currentTarget = this._currentTarget;
9297 this.altKey = e.altKey;
9298 this.ctrlKey = e.ctrlKey;
9299 this.metaKey = e.metaKey;
9300 this.shiftKey = e.shiftKey;
9301 this.type = (overrides && overrides.type) || e.type;
9302 this.clientX = e.clientX;
9303 this.clientY = e.clientY;
9308 c = e.keyCode || e.charCode;
9310 if (ua.webkit && (c in webkitKeymap)) {
9311 c = webkitKeymap[c];
9316 this.which = e.which || e.charCode || c;
9317 // this.button = e.button;
9318 this.button = this.which;
9320 this.target = resolve(e.target);
9321 this.currentTarget = resolve(currentTarget);
9322 this.relatedTarget = resolve(e.relatedTarget);
9324 if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
9325 this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
9329 this._touch(e, currentTarget, this._wrapper);
9333 stopPropagation: function() {
9334 this._event.stopPropagation();
9335 this._wrapper.stopped = 1;
9339 stopImmediatePropagation: function() {
9340 var e = this._event;
9341 if (e.stopImmediatePropagation) {
9342 e.stopImmediatePropagation();
9344 this.stopPropagation();
9346 this._wrapper.stopped = 2;
9350 preventDefault: function(returnValue) {
9351 var e = this._event;
9353 e.returnValue = returnValue || false;
9354 this._wrapper.prevented = 1;
9358 halt: function(immediate) {
9360 this.stopImmediatePropagation();
9362 this.stopPropagation();
9365 this.preventDefault();
9370 DOMEventFacade.resolve = resolve;
9371 Y.DOM2EventFacade = DOMEventFacade;
9372 Y.DOMEventFacade = DOMEventFacade;
9380 * The X location of the event on the page (including scroll)
9386 * The Y location of the event on the page (including scroll)
9392 * The keyCode for key events. Uses charCode if keyCode is not available
9398 * The charCode for key events. Same as keyCode
9399 * @property charCode
9404 * The button that was pushed.
9410 * The button that was pushed. Same as button.
9416 * Node reference for the targeted element
9422 * Node reference for the element that the listener was attached to.
9423 * @propery currentTarget
9428 * Node reference to the relatedTarget
9429 * @propery relatedTarget
9434 * Number representing the direction and velocity of the movement of the mousewheel.
9435 * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
9436 * @property wheelDelta
9441 * Stops the propagation to the next bubble target
9442 * @method stopPropagation
9446 * Stops the propagation to the next bubble target and
9447 * prevents any additional listeners from being exectued
9448 * on the current target.
9449 * @method stopImmediatePropagation
9453 * Prevents the event's default behavior
9454 * @method preventDefault
9455 * @param returnValue {string} sets the returnValue of the event to this value
9456 * (rather than the default false value). This can be used to add a customized
9457 * confirmation query to the beforeunload event).
9461 * Stops the event propagation and prevents the default
9464 * @param immediate {boolean} if true additional listeners
9465 * on the current target will not be executed
9469 * DOM event listener abstraction layer
9471 * @submodule event-base
9475 * The event utility provides functions to add and remove event listeners,
9476 * event cleansing. It also tries to automatically remove listeners it
9477 * registers during the unload event.
9483 Y.Env.evt.dom_wrappers = {};
9484 Y.Env.evt.dom_map = {};
9486 var _eventenv = Y.Env.evt,
9490 remove = YUI.Env.remove,
9492 onLoad = function() {
9493 YUI.Env.windowLoaded = true;
9495 remove(win, "load", onLoad);
9498 onUnload = function() {
9502 EVENT_READY = 'domready',
9504 COMPAT_ARG = '~yui|2|compat~',
9506 shouldIterate = function(o) {
9508 return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) &&
9509 !o.tagName && !o.alert);
9516 Event = function() {
9519 * True after the onload event has fired
9520 * @property _loadComplete
9525 var _loadComplete = false,
9528 * The number of times to poll after window.onload. This number is
9529 * increased if additional late-bound handlers are requested after
9531 * @property _retryCount
9538 * onAvailable listeners
9546 * Custom event wrappers for DOM events. Key is
9547 * 'event:' + Element uid stamp + event type
9548 * @property _wrappers
9549 * @type Y.Event.Custom
9553 _wrappers = _eventenv.dom_wrappers,
9555 _windowLoadKey = null,
9558 * Custom event wrapper map DOM events. Key is
9559 * Element uid stamp. Each item is a hash of custom event
9560 * wrappers as provided in the _wrappers collection. This
9561 * provides the infrastructure for getListeners.
9562 * @property _el_events
9566 _el_events = _eventenv.dom_map;
9571 * The number of times we should look for elements that are not
9572 * in the DOM at the time the event is requested after the document
9573 * has been loaded. The default is 1000@amp;40 ms, so it will poll
9574 * for 40 seconds or until all outstanding handlers are bound
9575 * (whichever comes first).
9576 * @property POLL_RETRYS
9584 * The poll interval in milliseconds
9585 * @property POLL_INTERVAL
9593 * addListener/removeListener can throw errors in unexpected scenarios.
9594 * These errors are suppressed, the method returns false, and this property
9596 * @property lastError
9605 * @property _interval
9612 * document readystate poll handle
9620 * True when the document is initially usable
9621 * @property DOMReady
9628 * @method startInterval
9632 startInterval: function() {
9633 if (!Event._interval) {
9634 Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL);
9639 * Executes the supplied callback when the item with the supplied
9640 * id is found. This is meant to be used to execute behavior as
9641 * soon as possible as the page loads. If you use this after the
9642 * initial page load it will poll for a fixed time for the element.
9643 * The number of times it will poll and the frequency are
9644 * configurable. By default it will poll for 10 seconds.
9646 * <p>The callback is executed with a single parameter:
9647 * the custom object parameter, if provided.</p>
9649 * @method onAvailable
9651 * @param {string||string[]} id the id of the element, or an array
9652 * of ids to look for.
9653 * @param {function} fn what to execute when the element is found.
9654 * @param {object} p_obj an optional object to be passed back as
9655 * a parameter to fn.
9656 * @param {boolean|object} p_override If set to true, fn will execute
9657 * in the context of p_obj, if set to an object it
9658 * will execute in the context of that object
9659 * @param checkContent {boolean} check child node readiness (onContentReady)
9661 * @deprecated Use Y.on("available")
9663 // @TODO fix arguments
9664 onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
9666 var a = Y.Array(id), i, availHandle;
9669 for (i=0; i<a.length; i=i+1) {
9674 override: p_override,
9675 checkReady: checkContent,
9679 _retryCount = this.POLL_RETRYS;
9681 // We want the first test to be immediate, but async
9682 setTimeout(Event._poll, 0);
9684 availHandle = new Y.EventHandle({
9686 _delete: function() {
9687 // set by the event system for lazy DOM listeners
9688 if (availHandle.handle) {
9689 availHandle.handle.detach();
9695 // otherwise try to remove the onAvailable listener(s)
9696 for (i = 0; i < a.length; i++) {
9697 for (j = 0; j < _avail.length; j++) {
9698 if (a[i] === _avail[j].id) {
9699 _avail.splice(j, 1);
9711 * Works the same way as onAvailable, but additionally checks the
9712 * state of sibling elements to determine if the content of the
9713 * available element is safe to modify.
9715 * <p>The callback is executed with a single parameter:
9716 * the custom object parameter, if provided.</p>
9718 * @method onContentReady
9720 * @param {string} id the id of the element to look for.
9721 * @param {function} fn what to execute when the element is ready.
9722 * @param {object} obj an optional object to be passed back as
9723 * a parameter to fn.
9724 * @param {boolean|object} override If set to true, fn will execute
9725 * in the context of p_obj. If an object, fn will
9726 * exectute in the context of that object
9729 * @deprecated Use Y.on("contentready")
9731 // @TODO fix arguments
9732 onContentReady: function(id, fn, obj, override, compat) {
9733 return Event.onAvailable(id, fn, obj, override, true, compat);
9737 * Adds an event listener
9741 * @param {String} type The type of event to append
9742 * @param {Function} fn The method the event invokes
9743 * @param {String|HTMLElement|Array|NodeList} el An id, an element
9744 * reference, or a collection of ids and/or elements to assign the
9746 * @param {Object} context optional context object
9747 * @param {Boolean|object} args 0..n arguments to pass to the callback
9748 * @return {EventHandle} an object to that can be used to detach the listener
9753 attach: function(type, fn, el, context) {
9754 return Event._attach(Y.Array(arguments, 0, true));
9757 _createWrapper: function (el, type, capture, compat, facade) {
9761 key = 'event:' + ek + type;
9763 if (false === facade) {
9771 cewrapper = _wrappers[key];
9775 // create CE wrapper
9776 cewrapper = Y.publish(key, {
9779 contextFn: function() {
9781 return cewrapper.el;
9783 cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
9784 return cewrapper.nodeRef;
9789 cewrapper.overrides = {};
9791 // for later removeListener calls
9793 cewrapper.key = key;
9794 cewrapper.domkey = ek;
9795 cewrapper.type = type;
9796 cewrapper.fn = function(e) {
9797 cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade))));
9799 cewrapper.capture = capture;
9801 if (el == win && type == "load") {
9802 // window load happens once
9803 cewrapper.fireOnce = true;
9804 _windowLoadKey = key;
9807 _wrappers[key] = cewrapper;
9808 _el_events[ek] = _el_events[ek] || {};
9809 _el_events[ek][key] = cewrapper;
9811 add(el, type, cewrapper.fn, capture);
9818 _attach: function(args, conf) {
9821 handles, oEl, cewrapper, context,
9822 fireNow = false, ret,
9825 el = args[2] || win,
9826 facade = conf && conf.facade,
9827 capture = conf && conf.capture,
9828 overrides = conf && conf.overrides;
9830 if (args[args.length-1] === COMPAT_ARG) {
9832 // trimmedArgs.pop();
9835 if (!fn || !fn.call) {
9836 // throw new TypeError(type + " attach call failed, callback undefined");
9840 // The el argument can be an array of elements or element ids.
9841 if (shouldIterate(el)) {
9845 Y.each(el, function(v, k) {
9847 handles.push(Event._attach(args, conf));
9850 // return (handles.length === 1) ? handles[0] : handles;
9851 return new Y.EventHandle(handles);
9853 // If the el argument is a string, we assume it is
9854 // actually the id of the element. If the page is loaded
9855 // we convert el to the actual element, otherwise we
9856 // defer attaching the event until the element is
9858 } else if (Y.Lang.isString(el)) {
9860 // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
9863 oEl = Y.DOM.byId(el);
9866 oEl = Y.Selector.query(el);
9868 switch (oEl.length) {
9877 return Event._attach(args, conf);
9885 // Not found = defer adding the event until the element is available
9888 ret = Event.onAvailable(el, function() {
9890 ret.handle = Event._attach(args, conf);
9892 }, Event, true, false, compat);
9899 // Element should be an html element or node
9904 if (Y.Node && Y.instanceOf(el, Y.Node)) {
9905 el = Y.Node.getDOMNode(el);
9908 cewrapper = Event._createWrapper(el, type, capture, compat, facade);
9910 Y.mix(cewrapper.overrides, overrides);
9913 if (el == win && type == "load") {
9915 // if the load is complete, fire immediately.
9916 // all subscribers, including the current one
9917 // will be notified.
9918 if (YUI.Env.windowLoaded) {
9929 // set context to the Node if not specified
9930 // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
9931 ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
9942 * Removes an event listener. Supports the signature the event was bound
9943 * with, but the preferred way to remove listeners is using the handle
9944 * that is returned when using Y.on
9948 * @param {String} type the type of event to remove.
9949 * @param {Function} fn the method the event invokes. If fn is
9950 * undefined, then all event handlers for the type of event are
9952 * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
9953 * event handle, an id, an element reference, or a collection
9954 * of ids and/or elements to remove the listener from.
9955 * @return {boolean} true if the unbind was successful, false otherwise.
9958 detach: function(type, fn, el, obj) {
9960 var args=Y.Array(arguments, 0, true), compat, l, ok, i,
9963 if (args[args.length-1] === COMPAT_ARG) {
9968 if (type && type.detach) {
9969 return type.detach();
9972 // The el argument can be a string
9973 if (typeof el == "string") {
9975 // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
9977 el = Y.DOM.byId(el);
9979 el = Y.Selector.query(el);
9983 } else if (l == 1) {
9987 // return Event.detach.apply(Event, args);
9996 return el.detach.apply(el, args);
9997 // The el argument can be an array of elements or element ids.
9998 } else if (shouldIterate(el)) {
10000 for (i=0, l=el.length; i<l; ++i) {
10002 ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
10008 if (!type || !fn || !fn.call) {
10009 return Event.purgeElement(el, false, type);
10012 id = 'event:' + Y.stamp(el) + type;
10013 ce = _wrappers[id];
10016 return ce.detach(fn);
10024 * Finds the event in the window object, the caller's arguments, or
10025 * in the arguments of another method in the callstack. This is
10026 * executed automatically for events registered through the event
10027 * manager, so the implementer should not normally need to execute
10028 * this function at all.
10030 * @param {Event} e the event parameter from the handler
10031 * @param {HTMLElement} el the element the listener was attached to
10032 * @return {Event} the event
10035 getEvent: function(e, el, noFacade) {
10036 var ev = e || win.event;
10038 return (noFacade) ? ev :
10039 new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
10043 * Generates an unique ID for the element if it does not already
10045 * @method generateId
10046 * @param el the element to create the id for
10047 * @return {string} the resulting id of the element
10050 generateId: function(el) {
10051 return Y.DOM.generateID(el);
10055 * We want to be able to use getElementsByTagName as a collection
10056 * to attach a group of events to. Unfortunately, different
10057 * browsers return different types of collections. This function
10058 * tests to determine if the object is array-like. It will also
10059 * fail if the object is an array, but is empty.
10060 * @method _isValidCollection
10061 * @param o the object to test
10062 * @return {boolean} true if the object is array-like and populated
10063 * @deprecated was not meant to be used directly
10067 _isValidCollection: shouldIterate,
10070 * hook up any deferred listeners
10075 _load: function(e) {
10076 if (!_loadComplete) {
10077 _loadComplete = true;
10079 // Just in case DOMReady did not go off for some reason
10082 Y.fire(EVENT_READY);
10085 // Available elements may not have been detected before the
10086 // window load event fires. Try to find them now so that the
10087 // the user is more likely to get the onAvailable notifications
10088 // before the window load notification
10094 * Polling function that runs before the onload event fires,
10095 * attempting to attach to DOM Nodes as soon as they are
10101 _poll: function() {
10102 if (Event.locked) {
10106 if (Y.UA.ie && !YUI.Env.DOMReady) {
10107 // Hold off if DOMReady has not fired and check current
10108 // readyState to protect against the IE operation aborted
10110 Event.startInterval();
10114 Event.locked = true;
10116 // keep trying until after the page is loaded. We need to
10117 // check the page load state prior to trying to bind the
10118 // elements so that we can be certain all elements have been
10119 // tested appropriately
10120 var i, len, item, el, notAvail, executeItem,
10121 tryAgain = !_loadComplete;
10124 tryAgain = (_retryCount > 0);
10130 executeItem = function (el, item) {
10131 var context, ov = item.override;
10133 if (item.override) {
10135 context = item.obj;
10142 item.fn.call(context, item.obj);
10144 context = item.obj || Y.one(el);
10145 item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
10150 for (i=0,len=_avail.length; i<len; ++i) {
10152 if (item && !item.checkReady) {
10154 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
10155 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
10158 executeItem(el, item);
10161 notAvail.push(item);
10167 for (i=0,len=_avail.length; i<len; ++i) {
10169 if (item && item.checkReady) {
10171 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
10172 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
10175 // The element is available, but not necessarily ready
10176 // @todo should we test parentNode.nextSibling?
10177 if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
10178 executeItem(el, item);
10182 notAvail.push(item);
10187 _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
10190 // we may need to strip the nulled out items here
10191 Event.startInterval();
10193 clearInterval(Event._interval);
10194 Event._interval = null;
10197 Event.locked = false;
10204 * Removes all listeners attached to the given element via addListener.
10205 * Optionally, the node's children can also be purged.
10206 * Optionally, you can specify a specific type of event to remove.
10207 * @method purgeElement
10208 * @param {HTMLElement} el the element to purge
10209 * @param {boolean} recurse recursively purge this element's children
10210 * as well. Use with caution.
10211 * @param {string} type optional type of listener to purge. If
10212 * left out, all listeners will be removed
10215 purgeElement: function(el, recurse, type) {
10216 // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
10217 var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
10218 lis = Event.getListeners(oEl, type), i, len, props, children, child;
10220 if (recurse && oEl) {
10222 children = Y.Selector.query('*', oEl);
10224 len = children.length;
10225 for (; i < len; ++i) {
10226 child = Event.getListeners(children[i], type);
10228 lis = lis.concat(child);
10236 for (; i < len; ++i) {
10239 remove(props.el, props.type, props.fn, props.capture);
10240 delete _wrappers[props.key];
10241 delete _el_events[props.domkey][props.key];
10249 * Returns all listeners attached to the given element via addListener.
10250 * Optionally, you can specify a specific type of event to return.
10251 * @method getListeners
10252 * @param el {HTMLElement|string} the element or element id to inspect
10253 * @param type {string} optional type of listener to return. If
10254 * left out, all listeners will be returned
10255 * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
10258 getListeners: function(el, type) {
10259 var ek = Y.stamp(el, true), evts = _el_events[ek],
10260 results=[] , key = (type) ? 'event:' + ek + type : null,
10261 adapters = _eventenv.plugins;
10268 // look for synthetic events
10269 if (adapters[type] && adapters[type].eventDef) {
10274 results.push(evts[key]);
10277 // get native events as well
10280 results.push(evts[key]);
10284 Y.each(evts, function(v, k) {
10289 return (results.length) ? results : null;
10293 * Removes all listeners registered by pe.event. Called
10294 * automatically during the unload event.
10299 _unload: function(e) {
10300 Y.each(_wrappers, function(v, k) {
10302 remove(v.el, v.type, v.fn, v.capture);
10303 delete _wrappers[k];
10304 delete _el_events[v.domkey][k];
10306 remove(win, "unload", onUnload);
10310 * Adds a DOM event directly without the caching, cleanup, context adj, etc
10312 * @method nativeAdd
10313 * @param {HTMLElement} el the element to bind the handler to
10314 * @param {string} type the type of event handler
10315 * @param {function} fn the callback to invoke
10316 * @param {boolen} capture capture or bubble phase
10323 * Basic remove listener
10325 * @method nativeRemove
10326 * @param {HTMLElement} el the element to bind the handler to
10327 * @param {string} type the type of event handler
10328 * @param {function} fn the callback to invoke
10329 * @param {boolen} capture capture or bubble phase
10333 nativeRemove: remove
10340 if (config.injected || YUI.Env.windowLoaded) {
10343 add(win, "load", onLoad);
10346 // Process onAvailable/onContentReady items when when the DOM is ready in IE
10348 Y.on(EVENT_READY, Event._poll);
10351 add(win, "unload", onUnload);
10353 Event.Custom = Y.CustomEvent;
10354 Event.Subscriber = Y.Subscriber;
10355 Event.Target = Y.EventTarget;
10356 Event.Handle = Y.EventHandle;
10357 Event.Facade = Y.EventFacade;
10364 * DOM event listener abstraction layer
10366 * @submodule event-base
10370 * Executes the callback as soon as the specified element
10371 * is detected in the DOM. This function expects a selector
10372 * string for the element(s) to detect. If you already have
10373 * an element reference, you don't need this event.
10375 * @param type {string} 'available'
10376 * @param fn {function} the callback function to execute.
10377 * @param el {string} an selector for the element(s) to attach
10378 * @param context optional argument that specifies what 'this' refers to.
10379 * @param args* 0..n additional arguments to pass on to the callback function.
10380 * These arguments will be added after the event object.
10381 * @return {EventHandle} the detach handle
10384 Y.Env.evt.plugins.available = {
10385 on: function(type, fn, id, o) {
10386 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null;
10387 return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
10392 * Executes the callback as soon as the specified element
10393 * is detected in the DOM with a nextSibling property
10394 * (indicating that the element's children are available).
10395 * This function expects a selector
10396 * string for the element(s) to detect. If you already have
10397 * an element reference, you don't need this event.
10398 * @event contentready
10399 * @param type {string} 'contentready'
10400 * @param fn {function} the callback function to execute.
10401 * @param el {string} an selector for the element(s) to attach.
10402 * @param context optional argument that specifies what 'this' refers to.
10403 * @param args* 0..n additional arguments to pass on to the callback function.
10404 * These arguments will be added after the event object.
10405 * @return {EventHandle} the detach handle
10408 Y.Env.evt.plugins.contentready = {
10409 on: function(type, fn, id, o) {
10410 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null;
10411 return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
10416 }, '3.3.0' ,{requires:['event-custom-base']});
10419 var stateChangeListener,
10420 GLOBAL_ENV = YUI.Env,
10421 config = YUI.config,
10423 docElement = doc && doc.documentElement,
10424 EVENT_NAME = 'onreadystatechange',
10425 pollInterval = config.pollInterval || 40;
10427 if (docElement.doScroll && !GLOBAL_ENV._ieready) {
10428 GLOBAL_ENV._ieready = function() {
10429 GLOBAL_ENV._ready();
10432 /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
10433 // Internet Explorer: use the doScroll() method on the root element.
10434 // This isolates what appears to be a safe moment to manipulate the
10435 // DOM prior to when the document's readyState suggests it is safe to do so.
10436 if (self !== self.top) {
10437 stateChangeListener = function() {
10438 if (doc.readyState == 'complete') {
10439 GLOBAL_ENV.remove(doc, EVENT_NAME, stateChangeListener);
10440 GLOBAL_ENV.ieready();
10443 GLOBAL_ENV.add(doc, EVENT_NAME, stateChangeListener);
10445 GLOBAL_ENV._dri = setInterval(function() {
10447 docElement.doScroll('left');
10448 clearInterval(GLOBAL_ENV._dri);
10449 GLOBAL_ENV._dri = null;
10450 GLOBAL_ENV._ieready();
10451 } catch (domNotReady) { }
10457 YUI.add('event-base-ie', function(Y) {
10460 * Custom event engine, DOM event listener abstraction layer, synthetic DOM
10463 * @submodule event-base
10466 var IEEventFacade = function() {
10467 // IEEventFacade.superclass.constructor.apply(this, arguments);
10468 Y.DOM2EventFacade.apply(this, arguments);
10471 Y.extend(IEEventFacade, Y.DOM2EventFacade, {
10475 IEEventFacade.superclass.init.apply(this, arguments);
10477 var e = this._event,
10478 resolve = Y.DOM2EventFacade.resolve,
10481 this.target = resolve(e.srcElement);
10483 if (('clientX' in e) && (!x) && (0 !== x)) {
10489 de = d.documentElement;
10491 x += (de.scrollLeft || (b && b.scrollLeft) || 0);
10492 y += (de.scrollTop || (b && b.scrollTop) || 0);
10498 if (e.type == "mouseout") {
10500 } else if (e.type == "mouseover") {
10504 this.relatedTarget = resolve(t);
10506 // which should contain the unicode key code if this is a key event
10507 // if (e.charCode) {
10508 // this.which = e.charCode;
10511 // for click events, which is normalized for which mouse button was
10514 switch (e.button) {
10522 this.which = e.button;
10525 this.button = this.which;
10530 stopPropagation: function() {
10531 var e = this._event;
10532 e.cancelBubble = true;
10533 this._wrapper.stopped = 1;
10537 stopImmediatePropagation: function() {
10538 this.stopPropagation();
10539 this._wrapper.stopped = 2;
10543 preventDefault: function(returnValue) {
10544 this._event.returnValue = returnValue || false;
10545 this._wrapper.prevented = 1;
10546 this.prevented = 1;
10551 var imp = Y.config.doc && Y.config.doc.implementation;
10553 if (imp && (!imp.hasFeature('Events', '2.0'))) {
10554 Y.DOMEventFacade = IEEventFacade;
10560 YUI.add('pluginhost-base', function(Y) {
10563 * Provides the augmentable PluginHost interface, which can be added to any class.
10564 * @module pluginhost
10568 * Provides the augmentable PluginHost interface, which can be added to any class.
10569 * @module pluginhost-base
10574 * An augmentable class, which provides the augmented class with the ability to host plugins.
10575 * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can
10576 * be used to add or remove plugins from instances of the class.
10579 * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using
10580 * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method.
10582 * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host):
10584 * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]});
10588 * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a>
10589 * methods should be invoked by the host class at the appropriate point in the host's lifecyle.
10592 * @class Plugin.Host
10597 function PluginHost() {
10598 this._plugins = {};
10601 PluginHost.prototype = {
10604 * Adds a plugin to the host object. This will instantiate the
10605 * plugin and attach it to the configured namespace on the host object.
10609 * @param P {Function | Object |Array} Accepts the plugin class, or an
10610 * object with a "fn" property specifying the plugin class and
10611 * a "cfg" property specifying the configuration for the Plugin.
10613 * Additionally an Array can also be passed in, with the above function or
10614 * object values, allowing the user to add multiple plugins in a single call.
10616 * @param config (Optional) If the first argument is the plugin class, the second argument
10617 * can be the configuration for the plugin.
10618 * @return {Base} A reference to the host object
10620 plug: function(Plugin, config) {
10623 if (L.isArray(Plugin)) {
10624 for (i = 0, ln = Plugin.length; i < ln; i++) {
10625 this.plug(Plugin[i]);
10628 if (Plugin && !L.isFunction(Plugin)) {
10629 config = Plugin.cfg;
10630 Plugin = Plugin.fn;
10633 // Plugin should be fn by now
10634 if (Plugin && Plugin.NS) {
10637 config = config || {};
10638 config.host = this;
10640 if (this.hasPlugin(ns)) {
10642 this[ns].setAttrs(config);
10644 // Create new instance
10645 this[ns] = new Plugin(config);
10646 this._plugins[ns] = Plugin;
10654 * Removes a plugin from the host object. This will destroy the
10655 * plugin instance and delete the namepsace from the host object.
10658 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided,
10659 * all registered plugins are unplugged.
10660 * @return {Base} A reference to the host object
10663 unplug: function(plugin) {
10665 plugins = this._plugins;
10668 if (L.isFunction(plugin)) {
10670 if (ns && (!plugins[ns] || plugins[ns] !== plugin)) {
10677 this[ns].destroy();
10681 delete plugins[ns];
10685 for (ns in this._plugins) {
10686 if (this._plugins.hasOwnProperty(ns)) {
10695 * Determines if a plugin has plugged into this host.
10697 * @method hasPlugin
10698 * @param {String} ns The plugin's namespace
10699 * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise.
10701 hasPlugin : function(ns) {
10702 return (this._plugins[ns] && this[ns]);
10706 * Initializes static plugins registered on the host (using the
10707 * Base.plug static method) and any plugins passed to the
10708 * instance through the "plugins" configuration property.
10710 * @method _initPlugins
10711 * @param {Config} config The configuration object with property name/value pairs.
10715 _initPlugins: function(config) {
10716 this._plugins = this._plugins || {};
10718 if (this._initConfigPlugins) {
10719 this._initConfigPlugins(config);
10724 * Unplugs and destroys all plugins on the host
10725 * @method _destroyPlugins
10728 _destroyPlugins: function() {
10733 Y.namespace("Plugin").Host = PluginHost;
10736 }, '3.3.0' ,{requires:['yui-base']});
10737 YUI.add('pluginhost-config', function(Y) {
10740 * Adds pluginhost constructor configuration and static configuration support
10741 * @submodule pluginhost-config
10745 * Constructor and static configuration support for plugins
10749 var PluginHost = Y.Plugin.Host,
10752 PluginHost.prototype._initConfigPlugins = function(config) {
10754 // Class Configuration
10755 var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
10758 constructor, i, classPlug, classUnplug, pluginClassName;
10760 // TODO: Room for optimization. Can we apply statically/unplug in same pass?
10761 for (i = classes.length - 1; i >= 0; i--) {
10762 constructor = classes[i];
10764 classUnplug = constructor._UNPLUG;
10766 // subclasses over-write
10767 Y.mix(unplug, classUnplug, true);
10770 classPlug = constructor._PLUG;
10772 // subclasses over-write
10773 Y.mix(plug, classPlug, true);
10777 for (pluginClassName in plug) {
10778 if (plug.hasOwnProperty(pluginClassName)) {
10779 if (!unplug[pluginClassName]) {
10780 this.plug(plug[pluginClassName]);
10785 // User Configuration
10786 if (config && config.plugins) {
10787 this.plug(config.plugins);
10792 * Registers plugins to be instantiated at the class level (plugins
10793 * which should be plugged into every instance of the class by default).
10795 * @method Plugin.Host.plug
10798 * @param {Function} hostClass The host class on which to register the plugins
10799 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
10800 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
10802 PluginHost.plug = function(hostClass, plugin, config) {
10803 // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ]
10806 if (hostClass !== Y.Base) {
10807 hostClass._PLUG = hostClass._PLUG || {};
10809 if (!L.isArray(plugin)) {
10811 plugin = {fn:plugin, cfg:config};
10816 for (i = 0, l = plugin.length; i < l;i++) {
10818 name = p.NAME || p.fn.NAME;
10819 hostClass._PLUG[name] = p;
10825 * Unregisters any class level plugins which have been registered by the host class, or any
10826 * other class in the hierarchy.
10828 * @method Plugin.Host.unplug
10831 * @param {Function} hostClass The host class from which to unregister the plugins
10832 * @param {Function | Array} plugin The plugin class, or an array of plugin classes
10834 PluginHost.unplug = function(hostClass, plugin) {
10837 if (hostClass !== Y.Base) {
10838 hostClass._UNPLUG = hostClass._UNPLUG || {};
10840 if (!L.isArray(plugin)) {
10844 for (i = 0, l = plugin.length; i < l; i++) {
10847 if (!hostClass._PLUG[name]) {
10848 hostClass._UNPLUG[name] = p;
10850 delete hostClass._PLUG[name];
10857 }, '3.3.0' ,{requires:['pluginhost-base']});
10860 YUI.add('pluginhost', function(Y){}, '3.3.0' ,{use:['pluginhost-base', 'pluginhost-config']});
10862 YUI.add('node-base', function(Y) {
10865 * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
10867 * @submodule node-base
10871 * The Node class provides a wrapper for manipulating DOM Nodes.
10872 * Node properties can be accessed via the set/get methods.
10873 * Use Y.get() to retrieve Node instances.
10875 * <strong>NOTE:</strong> Node properties are accessed using
10876 * the <code>set</code> and <code>get</code> methods.
10880 * @param {DOMNode} node the DOM node to be mapped to the Node instance.
10886 NODE_NAME = 'nodeName',
10887 NODE_TYPE = 'nodeType',
10888 OWNER_DOCUMENT = 'ownerDocument',
10889 TAG_NAME = 'tagName',
10892 _slice = Array.prototype.slice,
10896 Y_Node = function(node) {
10897 var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID];
10899 if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) {
10900 node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
10903 uid = uid || Y.stamp(node);
10904 if (!uid) { // stamp failed; likely IE non-HTMLElement
10911 * The underlying DOM node bound to the Y.Node instance
10916 Y_Node._instances[uid] = this;
10918 this._stateProxy = node; // when augmented with Attribute
10920 Y.EventTarget.call(this, {emitFacade:true});
10922 if (this._initPlugins) { // when augmented with Plugin.Host
10923 this._initPlugins();
10926 this.SHOW_TRANSITION = Y_Node.SHOW_TRANSITION;
10927 this.HIDE_TRANSITION = Y_Node.HIDE_TRANSITION;
10930 // used with previous/next/ancestor tests
10931 _wrapFn = function(fn) {
10934 ret = (typeof fn == 'string') ?
10936 return Y.Selector.test(n, fn);
10939 return fn(Y.one(n));
10948 * The name of the component
10952 Y_Node.NAME = 'node';
10955 * The pattern used to identify ARIA attributes
10957 Y_Node.re_aria = /^(?:role$|aria-)/;
10959 Y_Node.SHOW_TRANSITION = 'fadeIn';
10960 Y_Node.HIDE_TRANSITION = 'fadeOut';
10963 * List of events that route to DOM events
10965 * @property DOM_EVENTS
10968 Y_Node.DOM_EVENTS = {
10998 mousemultiwheel: 1,
11003 orientationchange: 1,
11014 // Add custom event adaptors to this list. This will make it so
11015 // that delegate, key, available, contentready, etc all will
11016 // be available through Node.on
11017 Y.mix(Y_Node.DOM_EVENTS, Y.Env.evt.plugins);
11020 * A list of Node instances that have been created
11022 * @property _instances
11026 Y_Node._instances = {};
11029 * Retrieves the DOM node bound to a Node instance
11030 * @method getDOMNode
11033 * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
11034 * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
11035 * as the node argument, it is simply returned.
11037 Y_Node.getDOMNode = function(node) {
11039 return (node.nodeType) ? node : node._node || null;
11045 * Checks Node return values and wraps DOM Nodes as Y.Node instances
11046 * and DOM Collections / Arrays as Y.NodeList instances.
11047 * Other return values just pass thru. If undefined is returned (e.g. no return)
11048 * then the Node instance is returned for chainability.
11052 * @param {any} node The Node instance or an HTMLNode
11053 * @return {Y.Node | Y.NodeList | any} Depends on what is returned from the DOM node.
11055 Y_Node.scrubVal = function(val, node) {
11056 if (val) { // only truthy values are risky
11057 if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function
11058 if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window
11060 } else if ((val.item && !val._nodes) || // dom collection or Node instance
11061 (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
11065 } else if (typeof val === 'undefined') {
11066 val = node; // for chaining
11067 } else if (val === null) {
11068 val = null; // IE: DOM null not the same as null
11075 * Adds methods to the Y.Node prototype, routing through scrubVal.
11076 * @method addMethod
11079 * @param {String} name The name of the method to add
11080 * @param {Function} fn The function that becomes the method
11081 * @param {Object} context An optional context to call the method with
11082 * (defaults to the Node instance)
11083 * @return {any} Depends on what is returned from the DOM node.
11085 Y_Node.addMethod = function(name, fn, context) {
11086 if (name && fn && typeof fn == 'function') {
11087 Y_Node.prototype[name] = function() {
11088 var args = _slice.call(arguments),
11092 if (args[0] && Y.instanceOf(args[0], Y_Node)) {
11093 args[0] = args[0]._node;
11096 if (args[1] && Y.instanceOf(args[1], Y_Node)) {
11097 args[1] = args[1]._node;
11099 args.unshift(node._node);
11101 ret = fn.apply(node, args);
11103 if (ret) { // scrub truthy
11104 ret = Y_Node.scrubVal(ret, node);
11107 (typeof ret != 'undefined') || (ret = node);
11115 * Imports utility methods to be added as Y.Node methods.
11116 * @method importMethod
11119 * @param {Object} host The object that contains the method to import.
11120 * @param {String} name The name of the method to import
11121 * @param {String} altName An optional name to use in place of the host name
11122 * @param {Object} context An optional context to call the method with
11124 Y_Node.importMethod = function(host, name, altName) {
11125 if (typeof name == 'string') {
11126 altName = altName || name;
11127 Y_Node.addMethod(altName, host[name], host);
11129 Y.Array.each(name, function(n) {
11130 Y_Node.importMethod(host, n);
11136 * Returns a single Node instance bound to the node or the
11137 * first element matching the given selector. Returns null if no match found.
11138 * <strong>Note:</strong> For chaining purposes you may want to
11139 * use <code>Y.all</code>, which returns a NodeList when no match is found.
11142 * @param {String | HTMLElement} node a node or Selector
11143 * @return {Y.Node | null} a Node instance or null if no match found.
11145 Y_Node.one = function(node) {
11146 var instance = null,
11151 if (typeof node == 'string') {
11152 if (node.indexOf('doc') === 0) { // doc OR document
11153 node = Y.config.doc;
11154 } else if (node.indexOf('win') === 0) { // win OR window
11155 node = Y.config.win;
11157 node = Y.Selector.query(node, null, true);
11162 } else if (Y.instanceOf(node, Y_Node)) {
11163 return node; // NOTE: return
11166 if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
11167 uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
11168 instance = Y_Node._instances[uid]; // reuse exising instances
11169 cachedNode = instance ? instance._node : null;
11170 if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
11171 instance = new Y_Node(node);
11179 * Returns a new dom node using the provided markup string.
11182 * @param {String} html The markup used to create the element
11183 * @param {HTMLDocument} doc An optional document context
11184 * @return {Node} A Node instance bound to a DOM node or fragment
11186 Y_Node.create = function(html, doc) {
11187 if (doc && doc._node) {
11190 return Y.one(Y_DOM.create(html, doc));
11194 * Static collection of configuration attributes for special handling
11201 * Allows for getting and setting the text of an element.
11202 * Formatting is preserved and special characters are treated literally.
11207 getter: function() {
11208 return Y_DOM.getText(this._node);
11211 setter: function(content) {
11212 Y_DOM.setText(this._node, content);
11218 getter: function() {
11219 return this._node.getElementsByTagName('option');
11224 * Returns a NodeList instance of all HTMLElement children.
11230 getter: function() {
11231 var node = this._node,
11232 children = node.children,
11233 childNodes, i, len;
11236 childNodes = node.childNodes;
11239 for (i = 0, len = childNodes.length; i < len; ++i) {
11240 if (childNodes[i][TAG_NAME]) {
11241 children[children.length] = childNodes[i];
11245 return Y.all(children);
11250 getter: function() {
11251 return Y_DOM.getValue(this._node);
11254 setter: function(val) {
11255 Y_DOM.setValue(this._node, val);
11262 * The default setter for DOM properties
11263 * Called with instance context (this === the Node instance)
11264 * @method DEFAULT_SETTER
11266 * @param {String} name The attribute/property being set
11267 * @param {any} val The value to be set
11268 * @return {any} The value
11270 Y_Node.DEFAULT_SETTER = function(name, val) {
11271 var node = this._stateProxy,
11274 if (name.indexOf(DOT) > -1) {
11276 name = name.split(DOT);
11277 // only allow when defined on node
11278 Y.Object.setValue(node, name, val);
11279 } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
11287 * The default getter for DOM properties
11288 * Called with instance context (this === the Node instance)
11289 * @method DEFAULT_GETTER
11291 * @param {String} name The attribute/property to look up
11292 * @return {any} The current value
11294 Y_Node.DEFAULT_GETTER = function(name) {
11295 var node = this._stateProxy,
11298 if (name.indexOf && name.indexOf(DOT) > -1) {
11299 val = Y.Object.getValue(node, name.split(DOT));
11300 } else if (typeof node[name] != 'undefined') { // pass thru from DOM
11307 // Basic prototype augment - no lazy constructor invocation.
11308 Y.mix(Y_Node, Y.EventTarget, false, null, 1);
11310 Y.mix(Y_Node.prototype, {
11312 * The method called when outputting Node instances as strings
11314 * @return {String} A string representation of the Node instance
11316 toString: function() {
11317 var str = this[UID] + ': not bound to a node',
11319 attrs, id, className;
11322 attrs = node.attributes;
11323 id = (attrs && attrs.id) ? node.getAttribute('id') : null;
11324 className = (attrs && attrs.className) ? node.getAttribute('className') : null;
11325 str = node[NODE_NAME];
11332 str += '.' + className.replace(' ', '.');
11336 str += ' ' + this[UID];
11342 * Returns an attribute value on the Node instance.
11343 * Unless pre-configured (via Node.ATTRS), get hands
11344 * off to the underlying DOM node. Only valid
11345 * attributes/properties for the node will be set.
11347 * @param {String} attr The attribute
11348 * @return {any} The current value of the attribute
11350 get: function(attr) {
11353 if (this._getAttr) { // use Attribute imple
11354 val = this._getAttr(attr);
11356 val = this._get(attr);
11360 val = Y_Node.scrubVal(val, this);
11361 } else if (val === null) {
11362 val = null; // IE: DOM null is not true null (even though they ===)
11368 * Helper method for get.
11371 * @param {String} attr The attribute
11372 * @return {any} The current value of the attribute
11374 _get: function(attr) {
11375 var attrConfig = Y_Node.ATTRS[attr],
11378 if (attrConfig && attrConfig.getter) {
11379 val = attrConfig.getter.call(this);
11380 } else if (Y_Node.re_aria.test(attr)) {
11381 val = this._node.getAttribute(attr, 2);
11383 val = Y_Node.DEFAULT_GETTER.apply(this, arguments);
11390 * Sets an attribute on the Node instance.
11391 * Unless pre-configured (via Node.ATTRS), set hands
11392 * off to the underlying DOM node. Only valid
11393 * attributes/properties for the node will be set.
11394 * To set custom attributes use setAttribute.
11396 * @param {String} attr The attribute to be set.
11397 * @param {any} val The value to set the attribute to.
11400 set: function(attr, val) {
11401 var attrConfig = Y_Node.ATTRS[attr];
11403 if (this._setAttr) { // use Attribute imple
11404 this._setAttr.apply(this, arguments);
11405 } else { // use setters inline
11406 if (attrConfig && attrConfig.setter) {
11407 attrConfig.setter.call(this, val, attr);
11408 } else if (Y_Node.re_aria.test(attr)) { // special case Aria
11409 this._node.setAttribute(attr, val);
11411 Y_Node.DEFAULT_SETTER.apply(this, arguments);
11419 * Sets multiple attributes.
11421 * @param {Object} attrMap an object of name/value pairs to set
11424 setAttrs: function(attrMap) {
11425 if (this._setAttrs) { // use Attribute imple
11426 this._setAttrs(attrMap);
11427 } else { // use setters inline
11428 Y.Object.each(attrMap, function(v, n) {
11437 * Returns an object containing the values for the requested attributes.
11439 * @param {Array} attrs an array of attributes to get values
11440 * @return {Object} An object with attribute name/value pairs.
11442 getAttrs: function(attrs) {
11444 if (this._getAttrs) { // use Attribute imple
11445 this._getAttrs(attrs);
11446 } else { // use setters inline
11447 Y.Array.each(attrs, function(v, n) {
11448 ret[v] = this.get(v);
11456 * Creates a new Node using the provided markup string.
11458 * @param {String} html The markup used to create the element
11459 * @param {HTMLDocument} doc An optional document context
11460 * @return {Node} A Node instance bound to a DOM node or fragment
11462 create: Y_Node.create,
11465 * Compares nodes to determine if they match.
11466 * Node instances can be compared to each other and/or HTMLElements.
11467 * @method compareTo
11468 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
11469 * @return {Boolean} True if the nodes match, false if they do not.
11471 compareTo: function(refNode) {
11472 var node = this._node;
11474 if (Y.instanceOf(refNode, Y_Node)) {
11475 refNode = refNode._node;
11477 return node === refNode;
11481 * Determines whether the node is appended to the document.
11483 * @param {Node|HTMLElement} doc optional An optional document to check against.
11484 * Defaults to current document.
11485 * @return {Boolean} Whether or not this node is appended to the document.
11487 inDoc: function(doc) {
11488 var node = this._node;
11489 doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
11490 if (doc.documentElement) {
11491 return Y_DOM.contains(doc.documentElement, node);
11495 getById: function(id) {
11496 var node = this._node,
11497 ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
11498 if (ret && Y_DOM.contains(node, ret)) {
11507 * Returns the nearest ancestor that passes the test applied by supplied boolean method.
11509 * @param {String | Function} fn A selector string or boolean method for testing elements.
11510 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
11511 * If a function is used, it receives the current node being tested as the only argument.
11512 * @return {Node} The matching Node instance or null if not found
11514 ancestor: function(fn, testSelf) {
11515 return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf));
11519 * Returns the ancestors that pass the test applied by supplied boolean method.
11520 * @method ancestors
11521 * @param {String | Function} fn A selector string or boolean method for testing elements.
11522 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
11523 * If a function is used, it receives the current node being tested as the only argument.
11524 * @return {NodeList} A NodeList instance containing the matching elements
11526 ancestors: function(fn, testSelf) {
11527 return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf));
11531 * Returns the previous matching sibling.
11532 * Returns the nearest element node sibling if no method provided.
11534 * @param {String | Function} fn A selector or boolean method for testing elements.
11535 * If a function is used, it receives the current node being tested as the only argument.
11536 * @return {Node} Node instance or null if not found
11538 previous: function(fn, all) {
11539 return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
11543 * Returns the next matching sibling.
11544 * Returns the nearest element node sibling if no method provided.
11546 * @param {String | Function} fn A selector or boolean method for testing elements.
11547 * If a function is used, it receives the current node being tested as the only argument.
11548 * @return {Node} Node instance or null if not found
11550 next: function(fn, all) {
11551 return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
11555 * Returns all matching siblings.
11556 * Returns all siblings if no method provided.
11558 * @param {String | Function} fn A selector or boolean method for testing elements.
11559 * If a function is used, it receives the current node being tested as the only argument.
11560 * @return {NodeList} NodeList instance bound to found siblings
11562 siblings: function(fn) {
11563 return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
11567 * Retrieves a Node instance of nodes based on the given CSS selector.
11570 * @param {string} selector The CSS selector to test against.
11571 * @return {Node} A Node instance for the matching HTMLElement.
11573 one: function(selector) {
11574 return Y.one(Y.Selector.query(selector, this._node, true));
11578 * Retrieves a nodeList based on the given CSS selector.
11581 * @param {string} selector The CSS selector to test against.
11582 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
11584 all: function(selector) {
11585 var nodelist = Y.all(Y.Selector.query(selector, this._node));
11586 nodelist._query = selector;
11587 nodelist._queryRoot = this._node;
11591 // TODO: allow fn test
11593 * Test if the supplied node matches the supplied selector.
11596 * @param {string} selector The CSS selector to test against.
11597 * @return {boolean} Whether or not the node matches the selector.
11599 test: function(selector) {
11600 return Y.Selector.test(this._node, selector);
11604 * Removes the node from its parent.
11605 * Shortcut for myNode.get('parentNode').removeChild(myNode);
11607 * @param {Boolean} destroy whether or not to call destroy() on the node
11612 remove: function(destroy) {
11613 var node = this._node,
11614 parentNode = node.parentNode;
11617 parentNode.removeChild(node);
11628 * Replace the node with the other node. This is a DOM update only
11629 * and does not change the node bound to the Node instance.
11630 * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
11632 * @param {Y.Node || HTMLNode} newNode Node to be inserted
11636 replace: function(newNode) {
11637 var node = this._node;
11638 if (typeof newNode == 'string') {
11639 newNode = Y_Node.create(newNode);
11641 node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
11646 * @method replaceChild
11648 * @param {String | HTMLElement | Node} node Node to be inserted
11649 * @param {HTMLElement | Node} refNode Node to be replaced
11650 * @return {Node} The replaced node
11652 replaceChild: function(node, refNode) {
11653 if (typeof node == 'string') {
11654 node = Y_DOM.create(node);
11657 return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
11661 * @method appendChild
11662 * @param {String | HTMLElement | Node} node Node to be appended
11663 * @return {Node} The appended node
11665 appendChild: function(node) {
11666 return Y_Node.scrubVal(this._insert(node));
11670 * @method insertBefore
11671 * @param {String | HTMLElement | Node} newNode Node to be appended
11672 * @param {HTMLElement | Node} refNode Node to be inserted before
11673 * @return {Node} The inserted node
11675 insertBefore: function(newNode, refNode) {
11676 return Y.Node.scrubVal(this._insert(newNode, refNode));
11680 * Removes event listeners from the node and (optionally) its subtree
11682 * @param {Boolean} recurse (optional) Whether or not to remove listeners from the
11684 * @param {String} type (optional) Only remove listeners of the specified type
11688 purge: function(recurse, type) {
11689 Y.Event.purgeElement(this._node, recurse, type);
11694 * Nulls internal node references, removes any plugins and event listeners
11696 * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
11697 * node's subtree (default is false)
11700 destroy: function(recursive) {
11701 this.purge(); // TODO: only remove events add via this Node
11703 if (this.unplug) { // may not be a PluginHost
11710 this.all('*').destroy();
11714 this._stateProxy = null;
11716 delete Y_Node._instances[this[UID]];
11720 * Invokes a method on the Node instance
11722 * @param {String} method The name of the method to invoke
11723 * @param {Any} a, b, c, etc. Arguments to invoke the method with.
11724 * @return Whatever the underly method returns.
11725 * DOM Nodes and Collections return values
11726 * are converted to Node/NodeList instances.
11729 invoke: function(method, a, b, c, d, e) {
11730 var node = this._node,
11733 if (a && Y.instanceOf(a, Y_Node)) {
11737 if (b && Y.instanceOf(b, Y_Node)) {
11741 ret = node[method](a, b, c, d, e);
11742 return Y_Node.scrubVal(ret, this);
11746 * Inserts the content before the reference node.
11748 * @param {String | Y.Node | HTMLElement | Y.NodeList | HTMLCollection} content The content to insert
11749 * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
11750 * Possible "where" arguments
11753 * <dd>The Node to insert before</dd>
11754 * <dt>HTMLElement</dt>
11755 * <dd>The element to insert before</dd>
11757 * <dd>The index of the child element to insert before</dd>
11758 * <dt>"replace"</dt>
11759 * <dd>Replaces the existing HTML</dd>
11760 * <dt>"before"</dt>
11761 * <dd>Inserts before the existing HTML</dd>
11762 * <dt>"before"</dt>
11763 * <dd>Inserts content before the node</dd>
11765 * <dd>Inserts content after the node</dd>
11769 insert: function(content, where) {
11770 this._insert(content, where);
11774 _insert: function(content, where) {
11775 var node = this._node,
11778 if (typeof where == 'number') { // allow index
11779 where = this._node.childNodes[where];
11780 } else if (where && where._node) { // Node
11781 where = where._node;
11784 if (content && typeof content != 'string') { // allow Node or NodeList/Array instances
11785 content = content._node || content._nodes || content;
11787 ret = Y_DOM.addHTML(node, content, where);
11793 * Inserts the content as the firstChild of the node.
11795 * @param {String | Y.Node | HTMLElement} content The content to insert
11798 prepend: function(content) {
11799 return this.insert(content, 0);
11803 * Inserts the content as the lastChild of the node.
11805 * @param {String | Y.Node | HTMLElement} content The content to insert
11808 append: function(content) {
11809 return this.insert(content, null);
11813 * Appends the node to the given node.
11815 * @param {Y.Node | HTMLElement} node The node to append to
11818 appendTo: function(node) {
11819 Y.one(node).append(this);
11823 * Replaces the node's current content with the content.
11824 * @method setContent
11825 * @param {String | Y.Node | HTMLElement | Y.NodeList | HTMLCollection} content The content to insert
11828 setContent: function(content) {
11829 this._insert(content, 'replace');
11834 * Returns the node's current content (e.g. innerHTML)
11835 * @method getContent
11836 * @return {String} The current content
11838 getContent: function(content) {
11839 return this.get('innerHTML');
11844 * @description Swap DOM locations with the given node.
11845 * This does not change which DOM node each Node instance refers to.
11846 * @param {Node} otherNode The node to swap with
11849 swap: Y.config.doc.documentElement.swapNode ?
11850 function(otherNode) {
11851 this._node.swapNode(Y_Node.getDOMNode(otherNode));
11853 function(otherNode) {
11854 otherNode = Y_Node.getDOMNode(otherNode);
11855 var node = this._node,
11856 parent = otherNode.parentNode,
11857 nextSibling = otherNode.nextSibling;
11859 if (nextSibling === node) {
11860 parent.insertBefore(node, otherNode);
11861 } else if (otherNode === node.nextSibling) {
11862 parent.insertBefore(otherNode, node);
11864 node.parentNode.replaceChild(otherNode, node);
11865 Y_DOM.addHTML(parent, node, nextSibling);
11873 * @description Retrieves arbitrary data stored on a Node instance.
11874 * This is not stored with the DOM node.
11875 * @param {string} name Optional name of the data field to retrieve.
11876 * If no name is given, all data is returned.
11877 * @return {any | Object} Whatever is stored at the given field,
11878 * or an object hash of all fields.
11880 getData: function(name) {
11882 this._data = this._data || {};
11883 if (arguments.length) {
11884 ret = this._data[name];
11895 * @description Stores arbitrary data on a Node instance.
11896 * This is not stored with the DOM node.
11897 * @param {string} name The name of the field to set. If no name
11898 * is given, name is treated as the data and overrides any existing data.
11899 * @param {any} val The value to be assigned to the field.
11902 setData: function(name, val) {
11903 this._data = this._data || {};
11904 if (arguments.length > 1) {
11905 this._data[name] = val;
11914 * @method clearData
11915 * @description Clears stored data.
11916 * @param {string} name The name of the field to clear. If no name
11917 * is given, all data is cleared.
11920 clearData: function(name) {
11921 if ('_data' in this) {
11923 delete this._data[name];
11932 hasMethod: function(method) {
11933 var node = this._node;
11934 return !!(node && method in node &&
11935 typeof node[method] != 'unknown' &&
11936 (typeof node[method] == 'function' ||
11937 String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space
11940 SHOW_TRANSITION: null,
11941 HIDE_TRANSITION: null,
11944 * Makes the node visible.
11945 * If the "transition" module is loaded, show optionally
11946 * animates the showing of the node using either the default
11947 * transition effect ('fadeIn'), or the given named effect.
11949 * @param {String} name A named Transition effect to use as the show effect.
11950 * @param {Object} config Options to use with the transition.
11951 * @param {Function} callback An optional function to run after the transition completes.
11954 show: function(callback) {
11955 callback = arguments[arguments.length - 1];
11956 this.toggleView(true, callback);
11961 * The implementation for showing nodes.
11962 * Default is to toggle the style.display property.
11966 _show: function() {
11967 this.setStyle('display', '');
11971 _isHidden: function() {
11972 return Y.DOM.getStyle(this._node, 'display') === 'none';
11975 toggleView: function(on, callback) {
11976 this._toggleView.apply(this, arguments);
11979 _toggleView: function(on, callback) {
11980 callback = arguments[arguments.length - 1];
11982 // base on current state if not forcing
11983 if (typeof on != 'boolean') {
11984 on = (this._isHidden()) ? 1 : 0;
11993 if (typeof callback == 'function') {
11994 callback.call(this);
12002 * If the "transition" module is loaded, hide optionally
12003 * animates the hiding of the node using either the default
12004 * transition effect ('fadeOut'), or the given named effect.
12006 * @param {String} name A named Transition effect to use as the show effect.
12007 * @param {Object} config Options to use with the transition.
12008 * @param {Function} callback An optional function to run after the transition completes.
12011 hide: function(callback) {
12012 callback = arguments[arguments.length - 1];
12013 this.toggleView(false, callback);
12018 * The implementation for hiding nodes.
12019 * Default is to toggle the style.display property.
12023 _hide: function() {
12024 this.setStyle('display', 'none');
12027 isFragment: function() {
12028 return (this.get('nodeType') === 11);
12032 * Removes all of the child nodes from the node.
12033 * @param {Boolean} destroy Whether the nodes should also be destroyed.
12036 empty: function(destroy) {
12037 this.get('childNodes').remove(destroy);
12044 Y.one = Y.Node.one;
12046 * The NodeList module provides support for managing collections of Nodes.
12048 * @submodule nodelist
12052 * The NodeList class provides a wrapper for manipulating DOM NodeLists.
12053 * NodeList properties can be accessed via the set/get methods.
12054 * Use Y.all() to retrieve NodeList instances.
12060 var NodeList = function(nodes) {
12062 if (typeof nodes === 'string') { // selector query
12063 this._query = nodes;
12064 nodes = Y.Selector.query(nodes);
12065 } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
12067 } else if (Y.instanceOf(nodes, Y.Node)) {
12068 nodes = [nodes._node];
12069 } else if (Y.instanceOf(nodes[0], Y.Node)) { // allow array of Y.Nodes
12070 Y.Array.each(nodes, function(node) {
12072 tmp.push(node._node);
12076 } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
12077 nodes = Y.Array(nodes, 0, true);
12081 * The underlying array of DOM nodes bound to the Y.NodeList instance
12085 this._nodes = nodes;
12088 NodeList.NAME = 'NodeList';
12091 * Retrieves the DOM nodes bound to a NodeList instance
12092 * @method NodeList.getDOMNodes
12095 * @param {Y.NodeList} nodelist The NodeList instance
12096 * @return {Array} The array of DOM nodes bound to the NodeList
12098 NodeList.getDOMNodes = function(nodelist) {
12099 return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
12102 NodeList.each = function(instance, fn, context) {
12103 var nodes = instance._nodes;
12104 if (nodes && nodes.length) {
12105 Y.Array.each(nodes, fn, context || instance);
12110 NodeList.addMethod = function(name, fn, context) {
12112 NodeList.prototype[name] = function() {
12116 Y.Array.each(this._nodes, function(node) {
12117 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid',
12118 instance = Y.Node._instances[node[UID]],
12123 instance = NodeList._getTempNode(node);
12125 ctx = context || instance;
12126 result = fn.apply(ctx, args);
12127 if (result !== undefined && result !== instance) {
12128 ret[ret.length] = result;
12132 // TODO: remove tmp pointer
12133 return ret.length ? ret : this;
12139 NodeList.importMethod = function(host, name, altName) {
12140 if (typeof name === 'string') {
12141 altName = altName || name;
12142 NodeList.addMethod(name, host[name]);
12144 Y.Array.each(name, function(n) {
12145 NodeList.importMethod(host, n);
12150 NodeList._getTempNode = function(node) {
12151 var tmp = NodeList._tempNode;
12153 tmp = Y.Node.create('<div></div>');
12154 NodeList._tempNode = tmp;
12158 tmp._stateProxy = node;
12162 Y.mix(NodeList.prototype, {
12164 * Retrieves the Node instance at the given index.
12167 * @param {Number} index The index of the target Node.
12168 * @return {Node} The Node instance at the given index.
12170 item: function(index) {
12171 return Y.one((this._nodes || [])[index]);
12175 * Applies the given function to each Node in the NodeList.
12177 * @param {Function} fn The function to apply. It receives 3 arguments:
12178 * the current node instance, the node's index, and the NodeList instance
12179 * @param {Object} context optional An optional context to apply the function with
12180 * Default context is the current Node instance
12183 each: function(fn, context) {
12184 var instance = this;
12185 Y.Array.each(this._nodes, function(node, index) {
12186 node = Y.one(node);
12187 return fn.call(context || node, node, index, instance);
12192 batch: function(fn, context) {
12193 var nodelist = this;
12195 Y.Array.each(this._nodes, function(node, index) {
12196 var instance = Y.Node._instances[node[UID]];
12198 instance = NodeList._getTempNode(node);
12201 return fn.call(context || instance, instance, index, nodelist);
12207 * Executes the function once for each node until a true value is returned.
12209 * @param {Function} fn The function to apply. It receives 3 arguments:
12210 * the current node instance, the node's index, and the NodeList instance
12211 * @param {Object} context optional An optional context to execute the function from.
12212 * Default context is the current Node instance
12213 * @return {Boolean} Whether or not the function returned true for any node.
12215 some: function(fn, context) {
12216 var instance = this;
12217 return Y.Array.some(this._nodes, function(node, index) {
12218 node = Y.one(node);
12219 context = context || node;
12220 return fn.call(context, node, index, instance);
12225 * Creates a documenFragment from the nodes bound to the NodeList instance
12227 * @return Node a Node instance bound to the documentFragment
12229 toFrag: function() {
12230 return Y.one(Y.DOM._nl2frag(this._nodes));
12234 * Returns the index of the node in the NodeList instance
12235 * or -1 if the node isn't found.
12237 * @param {Y.Node || DOMNode} node the node to search for
12238 * @return {Int} the index of the node value or -1 if not found
12240 indexOf: function(node) {
12241 return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
12245 * Filters the NodeList instance down to only nodes matching the given selector.
12247 * @param {String} selector The selector to filter against
12248 * @return {NodeList} NodeList containing the updated collection
12251 filter: function(selector) {
12252 return Y.all(Y.Selector.filter(this._nodes, selector));
12257 * Creates a new NodeList containing all nodes at every n indices, where
12258 * remainder n % index equals r.
12259 * (zero-based index).
12261 * @param {Int} n The offset to use (return every nth node)
12262 * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
12263 * @return {NodeList} NodeList containing the updated collection
12265 modulus: function(n, r) {
12268 NodeList.each(this, function(node, i) {
12274 return Y.all(nodes);
12278 * Creates a new NodeList containing all nodes at odd indices
12279 * (zero-based index).
12281 * @return {NodeList} NodeList containing the updated collection
12284 return this.modulus(2, 1);
12288 * Creates a new NodeList containing all nodes at even indices
12289 * (zero-based index), including zero.
12291 * @return {NodeList} NodeList containing the updated collection
12294 return this.modulus(2);
12297 destructor: function() {
12301 * Reruns the initial query, when created using a selector query
12305 refresh: function() {
12307 nodes = this._nodes,
12308 query = this._query,
12309 root = this._queryRoot;
12313 if (nodes && nodes[0] && nodes[0].ownerDocument) {
12314 root = nodes[0].ownerDocument;
12318 this._nodes = Y.Selector.query(query, root);
12324 _prepEvtArgs: function(type, fn, context) {
12325 // map to Y.on/after signature (type, fn, nodes, context, arg1, arg2, etc)
12326 var args = Y.Array(arguments, 0, true);
12328 if (args.length < 2) { // type only (event hash) just add nodes
12329 args[2] = this._nodes;
12331 args.splice(2, 0, this._nodes);
12334 args[3] = context || this; // default to NodeList instance as context
12340 * Applies an event listener to each Node bound to the NodeList.
12342 * @param {String} type The event being listened for
12343 * @param {Function} fn The handler to call when the event fires
12344 * @param {Object} context The context to call the handler with.
12345 * Default is the NodeList instance.
12346 * @param {Object} context The context to call the handler with.
12347 * param {mixed} arg* 0..n additional arguments to supply to the subscriber
12348 * when the event fires.
12349 * @return {Object} Returns an event handle that can later be use to detach().
12352 on: function(type, fn, context) {
12353 return Y.on.apply(Y, this._prepEvtArgs.apply(this, arguments));
12357 * Applies an one-time event listener to each Node bound to the NodeList.
12359 * @param {String} type The event being listened for
12360 * @param {Function} fn The handler to call when the event fires
12361 * @param {Object} context The context to call the handler with.
12362 * Default is the NodeList instance.
12363 * @return {Object} Returns an event handle that can later be use to detach().
12366 once: function(type, fn, context) {
12367 return Y.once.apply(Y, this._prepEvtArgs.apply(this, arguments));
12371 * Applies an event listener to each Node bound to the NodeList.
12372 * The handler is called only after all on() handlers are called
12373 * and the event is not prevented.
12375 * @param {String} type The event being listened for
12376 * @param {Function} fn The handler to call when the event fires
12377 * @param {Object} context The context to call the handler with.
12378 * Default is the NodeList instance.
12379 * @return {Object} Returns an event handle that can later be use to detach().
12382 after: function(type, fn, context) {
12383 return Y.after.apply(Y, this._prepEvtArgs.apply(this, arguments));
12387 * Returns the current number of items in the NodeList.
12389 * @return {Int} The number of items in the NodeList.
12392 return this._nodes.length;
12396 * Determines if the instance is bound to any nodes
12398 * @return {Boolean} Whether or not the NodeList is bound to any nodes
12400 isEmpty: function() {
12401 return this._nodes.length < 1;
12404 toString: function() {
12406 errorMsg = this[UID] + ': not bound to any nodes',
12407 nodes = this._nodes,
12410 if (nodes && nodes[0]) {
12412 str += node[NODE_NAME];
12414 str += '#' + node.id;
12417 if (node.className) {
12418 str += '.' + node.className.replace(' ', '.');
12421 if (nodes.length > 1) {
12422 str += '...[' + nodes.length + ' items]';
12425 return str || errorMsg;
12430 NodeList.importMethod(Y.Node.prototype, [
12432 * Called on each Node instance
12439 /** Called on each Node instance
12441 * @see Node.destroy
12446 * Called on each Node instance
12452 /** Called on each Node instance
12453 * @method detachAll
12454 * @see Node.detachAll
12458 /** Called on each Node instance
12464 /** Called on each Node instance
12470 /** Called on each Node instance
12472 * @see Node.prepend
12476 /** Called on each Node instance
12482 /** Called on each Node instance
12488 /** Called on each Node instance
12489 * @method setContent
12490 * @see Node.setContent
12495 * Makes each node visible.
12496 * If the "transition" module is loaded, show optionally
12497 * animates the showing of the node using either the default
12498 * transition effect ('fadeIn'), or the given named effect.
12500 * @param {String} name A named Transition effect to use as the show effect.
12501 * @param {Object} config Options to use with the transition.
12502 * @param {Function} callback An optional function to run after the transition completes.
12509 * If the "transition" module is loaded, hide optionally
12510 * animates the hiding of the node using either the default
12511 * transition effect ('fadeOut'), or the given named effect.
12513 * @param {String} name A named Transition effect to use as the show effect.
12514 * @param {Object} config Options to use with the transition.
12515 * @param {Function} callback An optional function to run after the transition completes.
12523 // one-off implementation to convert array of Nodes to NodeList
12524 // e.g. Y.all('input').get('parentNode');
12526 /** Called on each Node instance
12530 NodeList.prototype.get = function(attr) {
12532 nodes = this._nodes,
12533 isNodeList = false,
12534 getTemp = NodeList._getTempNode,
12539 instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
12540 val = instance._get(attr);
12541 if (val && val.nodeType) {
12546 Y.Array.each(nodes, function(node) {
12547 instance = Y.Node._instances[node._yuid];
12550 instance = getTemp(node);
12553 val = instance._get(attr);
12554 if (!isNodeList) { // convert array of Nodes to NodeList
12555 val = Y.Node.scrubVal(val, instance);
12561 return (isNodeList) ? Y.all(ret) : ret;
12564 Y.NodeList = NodeList;
12566 Y.all = function(nodes) {
12567 return new NodeList(nodes);
12570 Y.Node.all = Y.all;
12573 * Passes through to DOM method.
12575 * @method removeChild
12576 * @param {HTMLElement | Node} node Node to be removed
12577 * @return {Node} The removed node
12582 * Passes through to DOM method.
12583 * @method hasChildNodes
12584 * @return {Boolean} Whether or not the node has any childNodes
12589 * Passes through to DOM method.
12590 * @method cloneNode
12591 * @param {Boolean} deep Whether or not to perform a deep clone, which includes
12592 * subtree and attributes
12593 * @return {Node} The clone
12598 * Passes through to DOM method.
12599 * @method hasAttribute
12600 * @param {String} attribute The attribute to test for
12601 * @return {Boolean} Whether or not the attribute is present
12606 * Passes through to DOM method.
12607 * @method removeAttribute
12608 * @param {String} attribute The attribute to be removed
12614 * Passes through to DOM method.
12615 * @method scrollIntoView
12621 * Passes through to DOM method.
12622 * @method getElementsByTagName
12623 * @param {String} tagName The tagName to collect
12624 * @return {NodeList} A NodeList representing the HTMLCollection
12626 'getElementsByTagName',
12629 * Passes through to DOM method.
12636 * Passes through to DOM method.
12643 * Passes through to DOM method.
12644 * Only valid on FORM elements
12651 * Passes through to DOM method.
12652 * Only valid on FORM elements
12659 * Passes through to DOM method.
12666 * Passes through to DOM method.
12667 * Only valid on TABLE elements
12668 * @method createCaption
12673 ], function(method) {
12674 Y.Node.prototype[method] = function(arg1, arg2, arg3) {
12675 var ret = this.invoke(method, arg1, arg2, arg3);
12680 Y.Node.importMethod(Y.DOM, [
12682 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
12684 * @param {Node | HTMLElement} needle The possible node or descendent
12685 * @return {Boolean} Whether or not this node is the needle its ancestor
12689 * Allows setting attributes on DOM nodes, normalizing in some cases.
12690 * This passes through to the DOM node, allowing for custom attributes.
12691 * @method setAttribute
12695 * @param {string} name The attribute name
12696 * @param {string} value The value to set
12700 * Allows getting attributes on DOM nodes, normalizing in some cases.
12701 * This passes through to the DOM node, allowing for custom attributes.
12702 * @method getAttribute
12705 * @param {string} name The attribute name
12706 * @return {string} The attribute value
12711 * Wraps the given HTML around the node.
12713 * @param {String} html The markup to wrap around the node.
12719 * Removes the node's parent node.
12726 * Applies a unique ID to the node if none exists
12727 * @method generateID
12728 * @return {String} The existing or generated ID
12733 Y.NodeList.importMethod(Y.Node.prototype, [
12735 * Allows getting attributes on DOM nodes, normalizing in some cases.
12736 * This passes through to the DOM node, allowing for custom attributes.
12737 * @method getAttribute
12740 * @param {string} name The attribute name
12741 * @return {string} The attribute value
12746 * Allows setting attributes on DOM nodes, normalizing in some cases.
12747 * This passes through to the DOM node, allowing for custom attributes.
12748 * @method setAttribute
12752 * @param {string} name The attribute name
12753 * @param {string} value The value to set
12758 * Allows for removing attributes on DOM nodes.
12759 * This passes through to the DOM node, allowing for custom attributes.
12760 * @method removeAttribute
12763 * @param {string} name The attribute to remove
12767 * Removes the parent node from node in the list.
12773 * Wraps the given HTML around each node.
12775 * @param {String} html The markup to wrap around the node.
12781 * Applies a unique ID to each node if none exists
12782 * @method generateID
12783 * @return {String} The existing or generated ID
12790 * Determines whether each node has the given className.
12793 * @param {String} className the class name to search for
12794 * @return {Boolean} Whether or not the element has the specified class
12799 * Adds a class name to each node.
12801 * @param {String} className the class name to add to the node's class attribute
12807 * Removes a class name from each node.
12808 * @method removeClass
12809 * @param {String} className the class name to remove from the node's class attribute
12815 * Replace a class with another class for each node.
12816 * If no oldClassName is present, the newClassName is simply added.
12817 * @method replaceClass
12818 * @param {String} oldClassName the class name to be replaced
12819 * @param {String} newClassName the class name that will be replacing the old class name
12825 * If the className exists on the node it is removed, if it doesn't exist it is added.
12826 * @method toggleClass
12827 * @param {String} className the class name to be toggled
12828 * @param {Boolean} force Option to force adding or removing the class.
12834 Y.Node.importMethod(Y.DOM, methods);
12836 * Determines whether each node has the given className.
12838 * @see Node.hasClass
12840 * @param {String} className the class name to search for
12841 * @return {Array} An array of booleans for each node bound to the NodeList.
12845 * Adds a class name to each node.
12847 * @see Node.addClass
12848 * @param {String} className the class name to add to the node's class attribute
12853 * Removes a class name from each node.
12854 * @method removeClass
12855 * @see Node.removeClass
12856 * @param {String} className the class name to remove from the node's class attribute
12861 * Replace a class with another class for each node.
12862 * If no oldClassName is present, the newClassName is simply added.
12863 * @method replaceClass
12864 * @see Node.replaceClass
12865 * @param {String} oldClassName the class name to be replaced
12866 * @param {String} newClassName the class name that will be replacing the old class name
12871 * If the className exists on the node it is removed, if it doesn't exist it is added.
12872 * @method toggleClass
12873 * @see Node.toggleClass
12874 * @param {String} className the class name to be toggled
12877 Y.NodeList.importMethod(Y.Node.prototype, methods);
12880 if (!Y.config.doc.documentElement.hasAttribute) { // IE < 8
12881 Y.Node.prototype.hasAttribute = function(attr) {
12882 if (attr === 'value') {
12883 if (this.get('value') !== "") { // IE < 8 fails to populate specified when set in HTML
12887 return !!(this._node.attributes[attr] &&
12888 this._node.attributes[attr].specified);
12892 // IE throws an error when calling focus() on an element that's invisible, not
12893 // displayed, or disabled.
12894 Y.Node.prototype.focus = function () {
12896 this._node.focus();
12901 // IE throws error when setting input.type = 'hidden',
12902 // input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden'
12903 Y.Node.ATTRS.type = {
12904 setter: function(val) {
12905 if (val === 'hidden') {
12907 this._node.type = 'hidden';
12909 this.setStyle('display', 'none');
12910 this._inputType = 'hidden';
12913 try { // IE errors when changing the type from "hidden'
12914 this._node.type = val;
12921 getter: function() {
12922 return this._inputType || this._node.type;
12925 _bypassProxy: true // don't update DOM when using with Attribute
12928 if (Y.config.doc.createElement('form').elements.nodeType) {
12929 // IE: elements collection is also FORM node which trips up scrubVal.
12930 Y.Node.ATTRS.elements = {
12931 getter: function() {
12932 return this.all('input, textarea, button, select');
12937 Y.mix(Y.Node.ATTRS, {
12939 setter: function(h) {
12940 Y.DOM.setHeight(this._node, h);
12944 getter: function() {
12945 return this._node.offsetHeight;
12950 setter: function(w) {
12951 Y.DOM.setWidth(this._node, w);
12955 getter: function() {
12956 return this._node.offsetWidth;
12961 Y.mix(Y.Node.prototype, {
12962 sizeTo: function(w, h) {
12964 if (arguments.length < 2) {
12966 w = node.get('offsetWidth');
12967 h = node.get('offsetHeight');
12976 var Y_NodeList = Y.NodeList,
12977 ArrayProto = Array.prototype,
12979 /** Returns a new NodeList combining the given NodeList(s)
12982 * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to
12983 * concatenate to the resulting NodeList
12984 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
12987 /** Removes the first last from the NodeList and returns it.
12990 * @return {Node} The last item in the NodeList.
12993 /** Adds the given Node(s) to the end of the NodeList.
12996 * @param {Node | DOMNode} nodeN One or more nodes to add to the end of the NodeList.
12999 /** Removes the first item from the NodeList and returns it.
13002 * @return {Node} The first item in the NodeList.
13005 /** Returns a new NodeList comprising the Nodes in the given range.
13008 * @param {Number} begin Zero-based index at which to begin extraction.
13009 As a negative index, start indicates an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element in the sequence.
13010 * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end.
13011 slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).
13012 As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence.
13013 If end is omitted, slice extracts to the end of the sequence.
13014 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
13017 /** Changes the content of the NodeList, adding new elements while removing old elements.
13020 * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
13021 * @param {Number} howMany An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed. In this case, you should specify at least one new element. If no howMany parameter is specified (second syntax above, which is a SpiderMonkey extension), all elements after index are removed.
13022 * {Node | DOMNode| element1, ..., elementN
13023 The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array.
13024 * @return {NodeList} The element(s) removed.
13027 /** Adds the given Node(s) to the beginning of the NodeList.
13030 * @param {Node | DOMNode} nodeN One or more nodes to add to the NodeList.
13036 Y.Array.each(ArrayMethods, function(name) {
13037 Y_NodeList.prototype[name] = function() {
13042 while ((arg = arguments[i++])) { // use DOM nodes/nodeLists
13043 args.push(arg._node || arg._nodes || arg);
13045 return Y.Node.scrubVal(ArrayProto[name].apply(this._nodes, args));
13050 }, '3.3.0' ,{requires:['dom-base', 'selector-css2', 'event-base']});
13051 YUI.add('node-style', function(Y) {
13055 * Extended Node interface for managing node styles.
13057 * @submodule node-style
13062 * Returns the style's current value.
13065 * @param {String} attr The style attribute to retrieve.
13066 * @return {String} The current value of the style property for the element.
13071 * Returns the computed value for the given style property.
13072 * @method getComputedStyle
13073 * @param {String} attr The style attribute to retrieve.
13074 * @return {String} The computed value of the style property for the element.
13076 'getComputedStyle',
13079 * Sets a style property of the node.
13081 * @param {String} attr The style attribute to set.
13082 * @param {String|Number} val The value.
13088 * Sets multiple style properties on the node.
13089 * @method setStyles
13090 * @param {Object} hash An object literal of property:value pairs.
13095 Y.Node.importMethod(Y.DOM, methods);
13097 * Returns an array of values for each node.
13100 * @see Node.getStyle
13101 * @param {String} attr The style attribute to retrieve.
13102 * @return {Array} The current values of the style property for the element.
13106 * Returns an array of the computed value for each node.
13107 * @method getComputedStyle
13108 * @see Node.getComputedStyle
13109 * @param {String} attr The style attribute to retrieve.
13110 * @return {Array} The computed values for each node.
13114 * Sets a style property on each node.
13116 * @see Node.setStyle
13117 * @param {String} attr The style attribute to set.
13118 * @param {String|Number} val The value.
13123 * Sets multiple style properties on each node.
13124 * @method setStyles
13125 * @see Node.setStyles
13126 * @param {Object} hash An object literal of property:value pairs.
13129 Y.NodeList.importMethod(Y.Node.prototype, methods);
13133 }, '3.3.0' ,{requires:['dom-style', 'node-base']});
13134 YUI.add('node-screen', function(Y) {
13137 * Extended Node interface for managing regions and screen positioning.
13138 * Adds support for positioning elements and normalizes window size and scroll detection.
13140 * @submodule node-screen
13143 // these are all "safe" returns, no wrapping required
13146 * Returns the inner width of the viewport (exludes scrollbar).
13154 * Returns the inner height of the viewport (exludes scrollbar).
13155 * @config winHeight
13162 * @config winHeight
13169 * @config docHeight
13175 * Amount page has been scroll vertically
13176 * @config docScrollX
13182 * Amount page has been scroll horizontally
13183 * @config docScrollY
13189 Y.Node.ATTRS[name] = {
13190 getter: function() {
13191 var args = Array.prototype.slice.call(arguments);
13192 args.unshift(Y.Node.getDOMNode(this));
13194 return Y.DOM[name].apply(this, args);
13200 Y.Node.ATTRS.scrollLeft = {
13201 getter: function() {
13202 var node = Y.Node.getDOMNode(this);
13203 return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
13206 setter: function(val) {
13207 var node = Y.Node.getDOMNode(this);
13209 if ('scrollLeft' in node) {
13210 node.scrollLeft = val;
13211 } else if (node.document || node.nodeType === 9) {
13212 Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
13219 Y.Node.ATTRS.scrollTop = {
13220 getter: function() {
13221 var node = Y.Node.getDOMNode(this);
13222 return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
13225 setter: function(val) {
13226 var node = Y.Node.getDOMNode(this);
13228 if ('scrollTop' in node) {
13229 node.scrollTop = val;
13230 } else if (node.document || node.nodeType === 9) {
13231 Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
13238 Y.Node.importMethod(Y.DOM, [
13240 * Gets the current position of the node in page coordinates.
13243 * @return {Array} The XY position of the node
13248 * Set the position of the node in page coordinates, regardless of how the node is positioned.
13250 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
13256 * Gets the current position of the node in page coordinates.
13258 * @return {Int} The X position of the node
13263 * Set the position of the node in page coordinates, regardless of how the node is positioned.
13265 * @param {Int} x X value for new position (coordinates are page-based)
13271 * Gets the current position of the node in page coordinates.
13273 * @return {Int} The Y position of the node
13278 * Set the position of the node in page coordinates, regardless of how the node is positioned.
13280 * @param {Int} y Y value for new position (coordinates are page-based)
13286 * Swaps the XY position of this node with another node.
13288 * @param {Y.Node || HTMLElement} otherNode The node to swap with.
13295 * Returns a region object for the node
13300 Y.Node.ATTRS.region = {
13301 getter: function() {
13302 var node = Y.Node.getDOMNode(this),
13305 if (node && !node.tagName) {
13306 if (node.nodeType === 9) { // document
13307 node = node.documentElement;
13311 region = Y.DOM.viewportRegion(node);
13313 region = Y.DOM.region(node);
13320 * Returns a region object for the node's viewport
13321 * @config viewportRegion
13324 Y.Node.ATTRS.viewportRegion = {
13325 getter: function() {
13326 return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
13330 Y.Node.importMethod(Y.DOM, 'inViewportRegion');
13332 // these need special treatment to extract 2nd node arg
13334 * Compares the intersection of the node with another node or region
13335 * @method intersect
13337 * @param {Node|Object} node2 The node or region to compare with.
13338 * @param {Object} altRegion An alternate region to use (rather than this node's).
13339 * @return {Object} An object representing the intersection of the regions.
13341 Y.Node.prototype.intersect = function(node2, altRegion) {
13342 var node1 = Y.Node.getDOMNode(this);
13343 if (Y.instanceOf(node2, Y.Node)) { // might be a region object
13344 node2 = Y.Node.getDOMNode(node2);
13346 return Y.DOM.intersect(node1, node2, altRegion);
13350 * Determines whether or not the node is within the giving region.
13352 * @param {Node|Object} node2 The node or region to compare with.
13353 * @param {Boolean} all Whether or not all of the node must be in the region.
13354 * @param {Object} altRegion An alternate region to use (rather than this node's).
13355 * @return {Object} An object representing the intersection of the regions.
13357 Y.Node.prototype.inRegion = function(node2, all, altRegion) {
13358 var node1 = Y.Node.getDOMNode(this);
13359 if (Y.instanceOf(node2, Y.Node)) { // might be a region object
13360 node2 = Y.Node.getDOMNode(node2);
13362 return Y.DOM.inRegion(node1, node2, all, altRegion);
13366 }, '3.3.0' ,{requires:['dom-screen']});
13367 YUI.add('node-pluginhost', function(Y) {
13370 * Registers plugins to be instantiated at the class level (plugins
13371 * which should be plugged into every instance of Node by default).
13373 * @method Node.plug
13376 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
13377 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
13379 Y.Node.plug = function() {
13380 var args = Y.Array(arguments);
13381 args.unshift(Y.Node);
13382 Y.Plugin.Host.plug.apply(Y.Base, args);
13387 * Unregisters any class level plugins which have been registered by the Node
13389 * @method Node.unplug
13392 * @param {Function | Array} plugin The plugin class, or an array of plugin classes
13394 Y.Node.unplug = function() {
13395 var args = Y.Array(arguments);
13396 args.unshift(Y.Node);
13397 Y.Plugin.Host.unplug.apply(Y.Base, args);
13401 Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
13403 // allow batching of plug/unplug via NodeList
13404 // doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
13405 Y.NodeList.prototype.plug = function() {
13406 var args = arguments;
13407 Y.NodeList.each(this, function(node) {
13408 Y.Node.prototype.plug.apply(Y.one(node), args);
13412 Y.NodeList.prototype.unplug = function() {
13413 var args = arguments;
13414 Y.NodeList.each(this, function(node) {
13415 Y.Node.prototype.unplug.apply(Y.one(node), args);
13420 }, '3.3.0' ,{requires:['node-base', 'pluginhost']});
13421 YUI.add('node-event-delegate', function(Y) {
13424 * Functionality to make the node a delegated event container
13426 * @submodule node-event-delegate
13430 * <p>Sets up a delegation listener for an event occurring inside the Node.
13431 * The delegated event will be verified against a supplied selector or
13432 * filtering function to test if the event references at least one node that
13433 * should trigger the subscription callback.</p>
13435 * <p>Selector string filters will trigger the callback if the event originated
13436 * from a node that matches it or is contained in a node that matches it.
13437 * Function filters are called for each Node up the parent axis to the
13438 * subscribing container node, and receive at each level the Node and the event
13439 * object. The function should return true (or a truthy value) if that Node
13440 * should trigger the subscription callback. Note, it is possible for filters
13441 * to match multiple Nodes for a single event. In this case, the delegate
13442 * callback will be executed for each matching Node.</p>
13444 * <p>For each matching Node, the callback will be executed with its 'this'
13445 * object set to the Node matched by the filter (unless a specific context was
13446 * provided during subscription), and the provided event's
13447 * <code>currentTarget</code> will also be set to the matching Node. The
13448 * containing Node from which the subscription was originally made can be
13449 * referenced as <code>e.container</code>.
13452 * @param type {String} the event type to delegate
13453 * @param fn {Function} the callback function to execute. This function
13454 * will be provided the event object for the delegated event.
13455 * @param spec {String|Function} a selector that must match the target of the
13456 * event or a function to test target and its parents for a match
13457 * @param context {Object} optional argument that specifies what 'this' refers to.
13458 * @param args* {any} 0..n additional arguments to pass on to the callback function.
13459 * These arguments will be added after the event object.
13460 * @return {EventHandle} the detach handle
13463 Y.Node.prototype.delegate = function(type) {
13465 var args = Y.Array(arguments, 0, true),
13466 index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2;
13468 args.splice(index, 0, this._node);
13470 return Y.delegate.apply(Y, args);
13474 }, '3.3.0' ,{requires:['node-base', 'event-delegate']});
13477 YUI.add('node', function(Y){}, '3.3.0' ,{skinnable:false, requires:['dom', 'event-base', 'event-delegate', 'pluginhost'], use:['node-base', 'node-style', 'node-screen', 'node-pluginhost', 'node-event-delegate']});
13479 YUI.add('event-delegate', function(Y) {
13482 * Adds event delegation support to the library.
13485 * @submodule event-delegate
13488 var toArray = Y.Array,
13490 isString = YLang.isString,
13491 isObject = YLang.isObject,
13492 isArray = YLang.isArray,
13493 selectorTest = Y.Selector.test,
13494 detachCategories = Y.Env.evt.handles;
13497 * <p>Sets up event delegation on a container element. The delegated event
13498 * will use a supplied selector or filtering function to test if the event
13499 * references at least one node that should trigger the subscription
13502 * <p>Selector string filters will trigger the callback if the event originated
13503 * from a node that matches it or is contained in a node that matches it.
13504 * Function filters are called for each Node up the parent axis to the
13505 * subscribing container node, and receive at each level the Node and the event
13506 * object. The function should return true (or a truthy value) if that Node
13507 * should trigger the subscription callback. Note, it is possible for filters
13508 * to match multiple Nodes for a single event. In this case, the delegate
13509 * callback will be executed for each matching Node.</p>
13511 * <p>For each matching Node, the callback will be executed with its 'this'
13512 * object set to the Node matched by the filter (unless a specific context was
13513 * provided during subscription), and the provided event's
13514 * <code>currentTarget</code> will also be set to the matching Node. The
13515 * containing Node from which the subscription was originally made can be
13516 * referenced as <code>e.container</code>.
13519 * @param type {String} the event type to delegate
13520 * @param fn {Function} the callback function to execute. This function
13521 * will be provided the event object for the delegated event.
13522 * @param el {String|node} the element that is the delegation container
13523 * @param spec {string|Function} a selector that must match the target of the
13524 * event or a function to test target and its parents for a match
13525 * @param context optional argument that specifies what 'this' refers to.
13526 * @param args* 0..n additional arguments to pass on to the callback function.
13527 * These arguments will be added after the event object.
13528 * @return {EventHandle} the detach handle
13531 function delegate(type, fn, el, filter) {
13532 var args = toArray(arguments, 0, true),
13533 query = isString(el) ? el : null,
13534 typeBits, synth, container, categories, cat, i, len, handles, handle;
13536 // Support Y.delegate({ click: fnA, key: fnB }, context, filter, ...);
13537 // and Y.delegate(['click', 'key'], fn, context, filter, ...);
13538 if (isObject(type)) {
13541 if (isArray(type)) {
13542 for (i = 0, len = type.length; i < len; ++i) {
13544 handles.push(Y.delegate.apply(Y, args));
13547 // Y.delegate({'click', fn}, context, filter) =>
13548 // Y.delegate('click', fn, context, filter)
13549 args.unshift(null); // one arg becomes two; need to make space
13552 if (type.hasOwnProperty(i)) {
13555 handles.push(Y.delegate.apply(Y, args));
13560 return new Y.EventHandle(handles);
13563 typeBits = type.split(/\|/);
13565 if (typeBits.length > 1) {
13566 cat = typeBits.shift();
13567 type = typeBits.shift();
13570 synth = Y.Node.DOM_EVENTS[type];
13572 if (isObject(synth) && synth.delegate) {
13573 handle = synth.delegate.apply(synth, arguments);
13577 if (!type || !fn || !el || !filter) {
13581 container = (query) ? Y.Selector.query(query, null, true) : el;
13583 if (!container && isString(el)) {
13584 handle = Y.on('available', function () {
13585 Y.mix(handle, Y.delegate.apply(Y, args), true);
13589 if (!handle && container) {
13590 args.splice(2, 2, container); // remove the filter
13592 handle = Y.Event._attach(args, { facade: false });
13593 handle.sub.filter = filter;
13594 handle.sub._notify = delegate.notifySub;
13598 if (handle && cat) {
13599 categories = detachCategories[cat] || (detachCategories[cat] = {});
13600 categories = categories[type] || (categories[type] = []);
13601 categories.push(handle);
13608 * Overrides the <code>_notify</code> method on the normal DOM subscription to
13609 * inject the filtering logic and only proceed in the case of a match.
13611 * @method delegate.notifySub
13612 * @param thisObj {Object} default 'this' object for the callback
13613 * @param args {Array} arguments passed to the event's <code>fire()</code>
13614 * @param ce {CustomEvent} the custom event managing the DOM subscriptions for
13615 * the subscribed event on the subscribing node.
13616 * @return {Boolean} false if the event was stopped
13621 delegate.notifySub = function (thisObj, args, ce) {
13622 // Preserve args for other subscribers
13623 args = args.slice();
13625 args.push.apply(args, this.args);
13628 // Only notify subs if the event occurred on a targeted element
13629 var currentTarget = delegate._applyFilter(this.filter, args, ce),
13630 //container = e.currentTarget,
13633 if (currentTarget) {
13634 // Support multiple matches up the the container subtree
13635 currentTarget = toArray(currentTarget);
13637 // The second arg is the currentTarget, but we'll be reusing this
13638 // facade, replacing the currentTarget for each use, so it doesn't
13639 // matter what element we seed it with.
13640 e = args[0] = new Y.DOMEventFacade(args[0], ce.el, ce);
13642 e.container = Y.one(ce.el);
13644 for (i = 0, len = currentTarget.length; i < len && !e.stopped; ++i) {
13645 e.currentTarget = Y.one(currentTarget[i]);
13647 ret = this.fn.apply(this.context || e.currentTarget, args);
13649 if (ret === false) { // stop further notifications
13659 * <p>Compiles a selector string into a filter function to identify whether
13660 * Nodes along the parent axis of an event's target should trigger event
13661 * notification.</p>
13663 * <p>This function is memoized, so previously compiled filter functions are
13664 * returned if the same selector string is provided.</p>
13666 * <p>This function may be useful when defining synthetic events for delegate
13669 * @method delegate.compileFilter
13670 * @param selector {String} the selector string to base the filtration on
13671 * @return {Function}
13675 delegate.compileFilter = Y.cached(function (selector) {
13676 return function (target, e) {
13677 return selectorTest(target._node, selector, e.currentTarget._node);
13682 * Walks up the parent axis of an event's target, and tests each element
13683 * against a supplied filter function. If any Nodes, including the container,
13684 * satisfy the filter, the delegated callback will be triggered for each.
13686 * @method delegate._applyFilter
13687 * @param filter {Function} boolean function to test for inclusion in event
13689 * @param args {Array} the arguments that would be passed to subscribers
13690 * @param ce {CustomEvent} the DOM event wrapper
13691 * @return {Node|Node[]|undefined} The Node or Nodes that satisfy the filter
13694 delegate._applyFilter = function (filter, args, ce) {
13696 container = ce.el, // facadeless events in IE, have no e.currentTarget
13697 target = e.target || e.srcElement,
13699 isContainer = false;
13701 // Resolve text nodes to their containing element
13702 if (target.nodeType === 3) {
13703 target = target.parentNode;
13706 // passing target as the first arg rather than leaving well enough alone
13707 // making 'this' in the filter function refer to the target. This is to
13708 // support bound filter functions.
13709 args.unshift(target);
13711 if (isString(filter)) {
13713 isContainer = (target === container);
13714 if (selectorTest(target, filter, (isContainer ?null: container))) {
13715 match.push(target);
13722 target = target.parentNode;
13725 // filter functions are implementer code and should receive wrappers
13726 args[0] = Y.one(target);
13727 args[1] = new Y.DOMEventFacade(e, container, ce);
13730 // filter(target, e, extra args...) - this === target
13731 if (filter.apply(args[0], args)) {
13732 match.push(target);
13735 if (target === container) {
13739 target = target.parentNode;
13740 args[0] = Y.one(target);
13742 args[1] = e; // restore the raw DOM event
13745 if (match.length <= 1) {
13746 match = match[0]; // single match or undefined
13749 // remove the target
13756 * Sets up event delegation on a container element. The delegated event
13757 * will use a supplied filter to test if the callback should be executed.
13758 * This filter can be either a selector string or a function that returns
13759 * a Node to use as the currentTarget for the event.
13761 * The event object for the delegated event is supplied to the callback
13762 * function. It is modified slightly in order to support all properties
13763 * that may be needed for event delegation. 'currentTarget' is set to
13764 * the element that matched the selector string filter or the Node returned
13765 * from the filter function. 'container' is set to the element that the
13766 * listener is delegated from (this normally would be the 'currentTarget').
13768 * Filter functions will be called with the arguments that would be passed to
13769 * the callback function, including the event object as the first parameter.
13770 * The function should return false (or a falsey value) if the success criteria
13771 * aren't met, and the Node to use as the event's currentTarget and 'this'
13772 * object if they are.
13775 * @param type {string} the event type to delegate
13776 * @param fn {function} the callback function to execute. This function
13777 * will be provided the event object for the delegated event.
13778 * @param el {string|node} the element that is the delegation container
13779 * @param filter {string|function} a selector that must match the target of the
13780 * event or a function that returns a Node or false.
13781 * @param context optional argument that specifies what 'this' refers to.
13782 * @param args* 0..n additional arguments to pass on to the callback function.
13783 * These arguments will be added after the event object.
13784 * @return {EventHandle} the detach handle
13787 Y.delegate = Y.Event.delegate = delegate;
13790 }, '3.3.0' ,{requires:['node-base']});
13791 YUI.add('io-base', function(Y) {
13794 * Base IO functionality. Provides basic XHR transport support.
13796 * @submodule io-base
13800 * The io class is a utility that brokers HTTP requests through a simplified
13801 * interface. Specifically, it allows JavaScript to make HTTP requests to
13802 * a resource without a page reload. The underlying transport for making
13803 * same-domain requests is the XMLHttpRequest object. YUI.io can also use
13804 * Flash, if specified as a transport, for cross-domain requests.
13811 * @description This event is fired by YUI.io when a transaction is initiated.
13812 * @type Event Custom
13814 var E_START = 'io:start',
13817 * @event io:complete
13818 * @description This event is fired by YUI.io when a transaction is complete.
13819 * Response status and data are accessible, if available.
13820 * @type Event Custom
13822 E_COMPLETE = 'io:complete',
13825 * @event io:success
13826 * @description This event is fired by YUI.io when a transaction is complete, and
13827 * the HTTP status resolves to HTTP2xx.
13828 * @type Event Custom
13830 E_SUCCESS = 'io:success',
13833 * @event io:failure
13834 * @description This event is fired by YUI.io when a transaction is complete, and
13835 * the HTTP status resolves to HTTP4xx, 5xx and above.
13836 * @type Event Custom
13838 E_FAILURE = 'io:failure',
13842 * @description This event signifies the end of the transaction lifecycle. The
13843 * transaction transport is destroyed.
13844 * @type Event Custom
13848 //--------------------------------------
13850 //--------------------------------------
13852 * @description A transaction counter that increments for each transaction.
13854 * @property transactionId
13862 * @description Object of default HTTP headers to be initialized and sent
13863 * for all transactions.
13865 * @property _headers
13871 'X-Requested-With' : 'XMLHttpRequest'
13875 * @description Object that stores timeout values for any transaction with
13876 * a defined "timeout" configuration property.
13878 * @property _timeout
13885 // Window reference
13888 //--------------------------------------
13890 //--------------------------------------
13893 * @description Method that creates the XMLHttpRequest transport
13901 return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
13906 * @description Method that increments _transactionId for each transaction.
13914 var id = transactionId;
13922 * @description Method that creates a unique transaction object for each
13928 * @param {number} c - configuration object subset to determine if
13929 * the transaction is an XDR or file upload,
13930 * requiring an alternate transport.
13931 * @param {number} i - transaction id
13934 function _create(c, i) {
13936 o.id = Y.Lang.isNumber(i) ? i : _id();
13939 if (!c.use && !c.upload) {
13943 if (c.use === 'native') {
13944 if (w.XDomainRequest) {
13945 o.c = new XDomainRequest();
13953 o.c = Y.io._transport[c.use];
13965 function _destroy(o) {
13967 if (o.c && w.XMLHttpRequest) {
13968 o.c.onreadystatechange = null;
13970 else if (Y.UA.ie === 6 && !o.t) {
13971 // IE, when using XMLHttpRequest as an ActiveX Object, will throw
13972 // a "Type Mismatch" error if the event handler is set to "null".
13982 * @description Method for creating and subscribing transaction events.
13987 * @param {string} e - event to be published
13988 * @param {object} c - configuration data subset for event subscription.
13992 function _tE(e, c) {
13993 var eT = new Y.EventTarget().publish('transaction:' + e),
13995 cT = c.context || Y;
13998 eT.on(c.on[e], cT, a);
14001 eT.on(c.on[e], cT);
14008 * @description Fires event "io:start" and creates, fires a
14009 * transaction-specific start event, if config.on.start is
14015 * @param {number} id - transaction id
14016 * @param {object} c - configuration object for the transaction.
14020 function _ioStart(id, c) {
14021 var a = c.arguments;
14024 Y.fire(E_START, id, a);
14027 Y.fire(E_START, id);
14030 if (c.on && c.on.start) {
14031 _tE('start', c).fire(id);
14037 * @description Fires event "io:complete" and creates, fires a
14038 * transaction-specific "complete" event, if config.on.complete is
14041 * @method _ioComplete
14044 * @param {object} o - transaction object.
14045 * @param {object} c - configuration object for the transaction.
14049 function _ioComplete(o, c) {
14050 var r = o.e ? { status: 0, statusText: o.e } : o.c,
14054 Y.fire(E_COMPLETE, o.id, r, a);
14057 Y.fire(E_COMPLETE, o.id, r);
14060 if (c.on && c.on.complete) {
14061 _tE('complete', c).fire(o.id, r);
14066 * @description Fires event "io:end" and creates, fires a
14067 * transaction-specific "end" event, if config.on.end is
14073 * @param {object} o - transaction object.
14074 * @param {object} c - configuration object for the transaction.
14078 function _ioEnd(o, c) {
14079 var a = c.arguments;
14082 Y.fire(E_END, o.id, a);
14085 Y.fire(E_END, o.id);
14088 if (c.on && c.on.end) {
14089 _tE('end', c).fire(o.id);
14096 * @description Fires event "io:success" and creates, fires a
14097 * transaction-specific "success" event, if config.on.success is
14100 * @method _ioSuccess
14103 * @param {object} o - transaction object.
14104 * @param {object} c - configuration object for the transaction.
14108 function _ioSuccess(o, c) {
14109 var a = c.arguments;
14112 Y.fire(E_SUCCESS, o.id, o.c, a);
14115 Y.fire(E_SUCCESS, o.id, o.c);
14118 if (c.on && c.on.success) {
14119 _tE('success', c).fire(o.id, o.c);
14126 * @description Fires event "io:failure" and creates, fires a
14127 * transaction-specific "failure" event, if config.on.failure is
14130 * @method _ioFailure
14133 * @param {object} o - transaction object.
14134 * @param {object} c - configuration object for the transaction.
14138 function _ioFailure(o, c) {
14139 var r = o.e ? { status: 0, statusText: o.e } : o.c,
14143 Y.fire(E_FAILURE, o.id, r, a);
14146 Y.fire(E_FAILURE, o.id, r);
14149 if (c.on && c.on.failure) {
14150 _tE('failure', c).fire(o.id, r);
14157 * @description Resends an XDR transaction, using the Flash tranport,
14158 * if the native transport fails.
14164 * @param {object} o - Transaction object generated by _create().
14165 * @param {string} uri - qualified path to transaction resource.
14166 * @param {object} c - configuration object for the transaction.
14170 function _resend(o, uri, c, d) {
14172 c.xdr.use = 'flash';
14173 // If the original request included serialized form data and
14174 // additional data are defined in configuration.data, it must
14175 // be reset to prevent data duplication.
14176 c.data = c.form && d ? d : null;
14178 return Y.io(uri, c, o.id);
14182 * @description Method that concatenates string data for HTTP GET transactions.
14187 * @param {string} s - URI or root data.
14188 * @param {string} d - data to be concatenated onto URI.
14191 function _concat(s, d) {
14192 s += ((s.indexOf('?') == -1) ? '?' : '&') + d;
14197 * @description Method that stores default client headers for all transactions.
14198 * If a label is passed with no value argument, the header will be deleted.
14200 * @method _setHeader
14203 * @param {string} l - HTTP header
14204 * @param {string} v - HTTP header value
14207 function _setHeader(l, v) {
14212 delete _headers[l];
14217 * @description Method that sets all HTTP headers to be sent in a transaction.
14219 * @method _setHeaders
14222 * @param {object} o - XHR instance for the specific transaction.
14223 * @param {object} h - HTTP headers for the specific transaction, as defined
14224 * in the configuration object passed to YUI.io().
14227 function _setHeaders(o, h) {
14231 for (p in _headers) {
14232 if (_headers.hasOwnProperty(p)) {
14235 // Configuration headers will supersede preset io headers,
14236 // if headers match.
14240 h[p] = _headers[p];
14244 h[p] = _headers[p];
14250 if (h.hasOwnProperty(p)) {
14251 if (h[p] !== 'disable') {
14252 o.setRequestHeader(p, h[p]);
14259 * @description Terminates a transaction due to an explicit abort or
14262 * @method _ioCancel
14265 * @param {object} o - Transaction object generated by _create().
14266 * @param {string} s - Identifies timed out or aborted transaction.
14270 function _ioCancel(o, s) {
14278 * @description Starts timeout count if the configuration object
14279 * has a defined timeout property.
14281 * @method _startTimeout
14284 * @param {object} o - Transaction object generated by _create().
14285 * @param {object} t - Timeout in milliseconds.
14288 function _startTimeout(o, t) {
14289 _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, t);
14293 * @description Clears the timeout interval started by _startTimeout().
14295 * @method _clearTimeout
14298 * @param {number} id - Transaction id.
14301 function _clearTimeout(id) {
14302 w.clearTimeout(_timeout[id]);
14303 delete _timeout[id];
14307 * @description Method that determines if a transaction response qualifies
14308 * as success or failure, based on the response HTTP status code, and
14309 * fires the appropriate success or failure events.
14311 * @method _handleResponse
14314 * @param {object} o - Transaction object generated by _create().
14315 * @param {object} c - Configuration object passed to io().
14318 function _handleResponse(o, c) {
14322 status = (o.c.status && o.c.status !== 0) ? o.c.status : 0;
14328 // IE reports HTTP 204 as HTTP 1223.
14329 if (status >= 200 && status < 300 || status === 1223) {
14338 * @description Event handler bound to onreadystatechange.
14340 * @method _readyState
14343 * @param {object} o - Transaction object generated by _create().
14344 * @param {object} c - Configuration object passed to YUI.io().
14347 function _readyState(o, c) {
14348 if (o.c.readyState === 4) {
14350 _clearTimeout(o.id);
14356 _handleResponse(o, c);
14362 * @description Method for requesting a transaction. _io() is implemented as
14363 * yui.io(). Each transaction may include a configuration object. Its
14366 * method: HTTP method verb (e.g., GET or POST). If this property is not
14367 * not defined, the default value will be GET.
14369 * data: This is the name-value string that will be sent as the transaction
14370 * data. If the request is HTTP GET, the data become part of
14371 * querystring. If HTTP POST, the data are sent in the message body.
14373 * xdr: Defines the transport to be used for cross-domain requests. By
14374 * setting this property, the transaction will use the specified
14375 * transport instead of XMLHttpRequest.
14376 * The properties are:
14378 * use: Specify the transport to be used: 'flash' and 'native'
14379 * dataType: Set the value to 'XML' if that is the expected
14380 * response content type.
14384 * form: This is a defined object used to process HTML form as data. The
14387 * id: Node object or id of HTML form.
14388 * useDisabled: Boolean value to allow disabled HTML form field
14389 * values to be sent as part of the data.
14392 * on: This is a defined object used to create and handle specific
14393 * events during a transaction lifecycle. These events will fire in
14394 * addition to the global io events. The events are:
14395 * start - This event is fired when a request is sent to a resource.
14396 * complete - This event fires when the transaction is complete.
14397 * success - This event fires when the response status resolves to
14399 * failure - This event fires when the response status resolves to
14400 * HTTP 4xx, 5xx; and, for all transaction exceptions,
14401 * including aborted transactions and transaction timeouts.
14402 * end - This even is fired at the conclusion of the transaction
14403 * lifecycle, after a success or failure resolution.
14405 * The properties are:
14407 * start: function(id, arguments){},
14408 * complete: function(id, responseobject, arguments){},
14409 * success: function(id, responseobject, arguments){},
14410 * failure: function(id, responseobject, arguments){},
14411 * end: function(id, arguments){}
14413 * Each property can reference a function or be written as an
14416 * sync: To enable synchronous transactions, set the configuration property
14417 * "sync" to true; the default behavior is false. Synchronous
14418 * transactions are limited to same-domain requests only.
14420 * context: Object reference for all defined transaction event handlers
14421 * when it is implemented as a method of a base object. Defining
14422 * "context" will set the reference of "this," used in the
14423 * event handlers, to the context value. In the case where
14424 * different event handlers all have different contexts,
14425 * use Y.bind() to set the execution context, bypassing this
14428 * headers: This is a defined object of client headers, as many as.
14429 * desired for the transaction. The object pattern is:
14430 * { 'header': 'value' }.
14432 * timeout: This value, defined as milliseconds, is a time threshold for the
14433 * transaction. When this threshold is reached, and the transaction's
14434 * Complete event has not yet fired, the transaction will be aborted.
14436 * arguments: Object, array, string, or number passed to all registered
14437 * event handlers. This value is available as the second
14438 * argument in the "start" and "abort" event handlers; and, it is
14439 * the third argument in the "complete", "success", and "failure"
14445 * @param {string} uri - qualified path to transaction resource.
14446 * @param {object} c - configuration object for the transaction.
14447 * @param {number} i - transaction id, if already set.
14450 function _io(uri, c, i) {
14451 var f, o, d, m, r, s, oD, a, j,
14454 o = _create(c.xdr || c.form, i);
14455 m = c.method ? c.method = c.method.toUpperCase() : c.method = 'GET';
14459 //To serialize an object into a key-value string, add the
14460 //QueryString module to the YUI instance's 'use' method.
14461 if (Y.Lang.isObject(c.data) && Y.QueryString) {
14462 c.data = Y.QueryString.stringify(c.data);
14466 if (c.form.upload) {
14467 // This is a file upload transaction, calling
14468 // upload() in io-upload-iframe.
14469 return Y.io.upload(o, uri, c);
14472 // Serialize HTML form data.
14473 f = Y.io._serialize(c.form, c.data);
14474 if (m === 'POST' || m === 'PUT') {
14477 else if (m === 'GET') {
14478 uri = _concat(uri, f);
14483 if (c.data && m === 'GET') {
14484 uri = _concat(uri, c.data);
14487 if (c.data && m === 'POST') {
14488 c.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, c.headers);
14492 return Y.io.xdr(uri, o, c);
14496 o.c.onreadystatechange = function() { _readyState(o, c); };
14500 o.c.open(m, uri, s ? false : true);
14501 // Will work only in browsers that implement the
14502 // Cross-Origin Resource Sharing draft.
14503 if (c.xdr && c.xdr.credentials) {
14504 o.c.withCredentials = true;
14509 // This exception is usually thrown by browsers
14510 // that do not support native XDR transactions.
14511 return _resend(o, u, c, oD);
14515 _setHeaders(o.c, c.headers);
14518 // Using "null" with HTTP POST will result in a request
14519 // with no Content-Length header defined.
14520 o.c.send(c.data || '');
14523 a = ['status', 'statusText', 'responseText', 'responseXML'];
14524 r = c.arguments ? { id: o.id, arguments: c.arguments } : { id: o.id };
14526 for (j = 0; j < 4; j++) {
14527 r[a[j]] = o.c[a[j]];
14530 r.getAllResponseHeaders = function() { return d.getAllResponseHeaders(); };
14531 r.getResponseHeader = function(h) { return d.getResponseHeader(h); };
14533 _handleResponse(o, c);
14540 // This exception is usually thrown by browsers
14541 // that do not support native XDR transactions.
14542 return _resend(o, u, c, oD);
14546 // If config.timeout is defined, and the request is standard XHR,
14547 // initialize timeout polling.
14549 _startTimeout(o, c.timeout);
14554 abort: function() {
14555 return o.c ? _ioCancel(o, 'abort') : false;
14557 isInProgress: function() {
14558 return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
14563 _io.start = _ioStart;
14564 _io.complete = _ioComplete;
14565 _io.success = _ioSuccess;
14566 _io.failure = _ioFailure;
14569 _io._timeout = _timeout;
14571 //--------------------------------------
14572 // Begin public interface definition
14573 //--------------------------------------
14575 * @description Method that stores default client headers for all transactions.
14576 * If a label is passed with no value argument, the header will be deleted.
14577 * This is the interface for _setHeader().
14582 * @param {string} l - HTTP header
14583 * @param {string} v - HTTP header value
14586 _io.header = _setHeader;
14589 * @description Method for requesting a transaction. This
14590 * is the interface for _io().
14595 * @param {string} uri - qualified path to transaction resource.
14596 * @param {object} c - configuration object for the transaction.
14604 }, '3.3.0' ,{requires:['event-custom-base', 'querystring-stringify-simple']});
14605 YUI.add('querystring-stringify-simple', function(Y) {
14609 * <p>Provides Y.QueryString.stringify method for converting objects to Query Strings.
14610 * This is a subset implementation of the full querystring-stringify.</p>
14611 * <p>This module provides the bare minimum functionality (encoding a hash of simple values),
14612 * without the additional support for nested data structures. Every key-value pair is
14613 * encoded by encodeURIComponent.</p>
14614 * <p>This module provides a minimalistic way for io to handle single-level objects
14615 * as transaction data.</p>
14617 * @module querystring
14618 * @submodule querystring-stringify-simple
14623 var QueryString = Y.namespace("QueryString"),
14624 EUC = encodeURIComponent;
14627 * <p>Converts a simple object to a Query String representation.</p>
14628 * <p>Nested objects, Arrays, and so on, are not supported.</p>
14630 * @method stringify
14633 * @submodule querystring-stringify-simple
14634 * @param obj {Object} A single-level object to convert to a querystring.
14635 * @param cfg {Object} (optional) Configuration object. In the simple
14636 * module, only the arrayKey setting is
14637 * supported. When set to true, the key of an
14638 * array will have the '[]' notation appended
14642 QueryString.stringify = function (obj, c) {
14644 // Default behavior is false; standard key notation.
14645 s = c && c.arrayKey ? true : false,
14649 if (obj.hasOwnProperty(key)) {
14650 if (Y.Lang.isArray(obj[key])) {
14651 for (i = 0, l = obj[key].length; i < l; i++) {
14652 qs.push(EUC(s ? key + '[]' : key) + '=' + EUC(obj[key][i]));
14656 qs.push(EUC(key) + '=' + EUC(obj[key]));
14661 return qs.join('&');
14667 YUI.add('json-parse', function(Y) {
14670 * <p>The JSON module adds support for serializing JavaScript objects into
14671 * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
14673 * <p>The JSON namespace is added to your YUI instance including static methods
14674 * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
14676 * <p>The functionality and method signatures follow the ECMAScript 5
14677 * specification. In browsers with native JSON support, the native
14678 * implementation is used.</p>
14680 * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
14681 * <code>json-stringify</code>.</p>
14683 * <p>As their names suggest, <code>json-parse</code> adds support for parsing
14684 * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
14685 * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to
14686 * include either of the submodules individually if you don't need the
14687 * complementary functionality, or include the rollup for both.</p>
14695 * Provides Y.JSON.parse method to accept JSON strings and return native
14696 * JavaScript objects.
14699 * @submodule json-parse
14705 // All internals kept private for security reasons
14706 function fromGlobal(ref) {
14707 return (Y.config.win || this || {})[ref];
14712 * Alias to native browser implementation of the JSON object if available.
14718 var _JSON = fromGlobal('JSON'),
14719 // Create an indirect reference to eval to allow for minification
14720 _eval = fromGlobal('eval'),
14721 Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
14722 useNative = !!Native,
14725 * Replace certain Unicode characters that JavaScript may handle incorrectly
14726 * during eval--either by deleting them or treating them as line
14727 * endings--with escape sequences.
14728 * IMPORTANT NOTE: This regex will be used to modify the input if a match is
14731 * @property _UNICODE_EXCEPTIONS
14735 _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
14739 * First step in the safety evaluation. Regex used to replace all escape
14740 * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
14742 * @property _ESCAPES
14746 _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
14749 * Second step in the safety evaluation. Regex used to replace all simple
14750 * values with ']' characters.
14752 * @property _VALUES
14756 _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
14759 * Third step in the safety evaluation. Regex used to remove all open
14760 * square brackets following a colon, comma, or at the beginning of the
14763 * @property _BRACKETS
14767 _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
14770 * Final step in the safety evaluation. Regex used to test the string left
14771 * after all previous replacements for invalid characters.
14773 * @property _UNSAFE
14777 _UNSAFE = /[^\],:{}\s]/,
14780 * Replaces specific unicode characters with their appropriate \unnnn
14781 * format. Some browsers ignore certain characters during eval.
14783 * @method escapeException
14784 * @param c {String} Unicode character
14785 * @return {String} the \unnnn escapement of the character
14788 _escapeException = function (c) {
14789 return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
14793 * Traverses nested objects, applying a reviver function to each (key,value)
14794 * from the scope if the key:value's containing object. The value returned
14795 * from the function will replace the original value in the key:value pair.
14796 * If the value returned is undefined, the key will be omitted from the
14800 * @param data {MIXED} Any JavaScript data
14801 * @param reviver {Function} filter or mutation function
14802 * @return {MIXED} The results of the filtered data
14805 _revive = function (data, reviver) {
14806 var walk = function (o,key) {
14807 var k,v,value = o[key];
14808 if (value && typeof value === 'object') {
14810 if (value.hasOwnProperty(k)) {
14811 v = walk(value, k);
14812 if (v === undefined) {
14820 return reviver.call(o,key,value);
14823 return typeof reviver === 'function' ? walk({'':data},'') : data;
14827 * Parse a JSON string, returning the native JavaScript representation.
14829 * @param s {string} JSON string data
14830 * @param reviver {function} (optional) function(k,v) passed each key value
14831 * pair of object literals, allowing pruning or altering values
14832 * @return {MIXED} the native JavaScript representation of the JSON string
14833 * @throws SyntaxError
14837 // JavaScript implementation in lieu of native browser support. Based on
14838 // the json2.js library from http://json.org
14839 _parse = function (s,reviver) {
14840 // Replace certain Unicode characters that are otherwise handled
14841 // incorrectly by some browser implementations.
14842 // NOTE: This modifies the input if such characters are found!
14843 s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
14845 // Test for any remaining invalid characters
14846 if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
14847 replace(_VALUES,']').
14848 replace(_BRACKETS,''))) {
14850 // Eval the text into a JavaScript data structure, apply any
14851 // reviver function, and return
14852 return _revive( _eval('(' + s + ')'), reviver );
14855 throw new SyntaxError('JSON.parse');
14858 Y.namespace('JSON').parse = function (s,reviver) {
14859 if (typeof s !== 'string') {
14863 return Native && Y.JSON.useNativeParse ?
14864 Native.parse(s,reviver) : _parse(s,reviver);
14867 function workingNative( k, v ) {
14868 return k === "ok" ? true : v;
14871 // Double check basic functionality. This is mainly to catch early broken
14872 // implementations of the JSON API in Firefox 3.1 beta1 and beta2
14875 useNative = ( Native.parse( '{"ok":false}', workingNative ) ).ok;
14883 * Leverage native JSON parse if the browser has a native implementation.
14884 * In general, this is a good idea. See the Known Issues section in the
14885 * JSON user guide for caveats. The default value is true for browsers with
14886 * native JSON support.
14888 * @property useNativeParse
14893 Y.JSON.useNativeParse = useNative;
14897 YUI.add('transition-native', function(Y) {
14900 * Provides the transition method for Node.
14901 * Transition has no API of its own, but adds the transition method to Node.
14903 * @module transition
14904 * @requires node-style
14907 var TRANSITION = '-webkit-transition',
14908 TRANSITION_CAMEL = 'WebkitTransition',
14909 TRANSITION_PROPERTY_CAMEL = 'WebkitTransitionProperty',
14910 TRANSITION_PROPERTY = '-webkit-transition-property',
14911 TRANSITION_DURATION = '-webkit-transition-duration',
14912 TRANSITION_TIMING_FUNCTION = '-webkit-transition-timing-function',
14913 TRANSITION_DELAY = '-webkit-transition-delay',
14914 TRANSITION_END = 'webkitTransitionEnd',
14915 ON_TRANSITION_END = 'onwebkittransitionend',
14916 TRANSFORM_CAMEL = 'WebkitTransform',
14921 * A class for constructing transition instances.
14922 * Adds the "transition" method to Node.
14923 * @class Transition
14927 Transition = function() {
14928 this.init.apply(this, arguments);
14931 Transition.fx = {};
14932 Transition.toggles = {};
14934 Transition._hasEnd = {};
14936 Transition._toCamel = function(property) {
14937 property = property.replace(/-([a-z])/gi, function(m0, m1) {
14938 return m1.toUpperCase();
14944 Transition._toHyphen = function(property) {
14945 property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {
14948 str += '-' + m1.toLowerCase();
14953 str += '-' + m3.toLowerCase();
14963 Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
14965 Transition.useNative = false;
14967 if (TRANSITION in Y.config.doc.documentElement.style) {
14968 Transition.useNative = true;
14969 Transition.supported = true; // TODO: remove
14972 Y.Node.DOM_EVENTS[TRANSITION_END] = 1;
14974 Transition.NAME = 'transition';
14976 Transition.DEFAULT_EASING = 'ease';
14977 Transition.DEFAULT_DURATION = 0.5;
14978 Transition.DEFAULT_DELAY = 0;
14980 Transition._nodeAttrs = {};
14982 Transition.prototype = {
14983 constructor: Transition,
14984 init: function(node, config) {
14987 if (!anim._running && config) {
14988 anim._config = config;
14989 node._transition = anim; // cache for reuse
14991 anim._duration = ('duration' in config) ?
14992 config.duration: anim.constructor.DEFAULT_DURATION;
14994 anim._delay = ('delay' in config) ?
14995 config.delay: anim.constructor.DEFAULT_DELAY;
14997 anim._easing = config.easing || anim.constructor.DEFAULT_EASING;
14998 anim._count = 0; // track number of animated properties
14999 anim._running = false;
15006 addProperty: function(prop, config) {
15009 uid = Y.stamp(node),
15010 nodeInstance = Y.one(node),
15011 attrs = Transition._nodeAttrs[uid],
15019 attrs = Transition._nodeAttrs[uid] = {};
15022 attr = attrs[prop];
15024 // might just be a value
15025 if (config && config.value !== undefined) {
15026 val = config.value;
15027 } else if (config !== undefined) {
15029 config = EMPTY_OBJ;
15032 if (typeof val === 'function') {
15033 val = val.call(nodeInstance, nodeInstance);
15036 if (attr && attr.transition) {
15037 // take control if another transition owns this property
15038 if (attr.transition !== anim) {
15039 attr.transition._count--; // remapping attr to this transition
15043 anim._count++; // properties per transition
15045 // make 0 async and fire events
15046 dur = ((typeof config.duration != 'undefined') ? config.duration :
15047 anim._duration) || 0.0001;
15052 delay: (typeof config.delay != 'undefined') ? config.delay :
15055 easing: config.easing || anim._easing,
15060 // native end event doesnt fire when setting to same value
15061 // supplementing with timer
15062 // val may be a string or number (height: 0, etc), but computedStyle is always string
15063 computed = Y.DOM.getComputedStyle(node, prop);
15064 compareVal = (typeof val === 'string') ? computed : parseFloat(computed);
15066 if (Transition.useNative && compareVal === val) {
15067 setTimeout(function() {
15068 anim._onNativeEnd.call(node, {
15069 propertyName: prop,
15076 removeProperty: function(prop) {
15078 attrs = Transition._nodeAttrs[Y.stamp(anim._node)];
15080 if (attrs && attrs[prop]) {
15081 delete attrs[prop];
15087 initAttrs: function(config) {
15091 if (config.transform && !config[TRANSFORM_CAMEL]) {
15092 config[TRANSFORM_CAMEL] = config.transform;
15093 delete config.transform; // TODO: copy
15096 for (attr in config) {
15097 if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) {
15098 this.addProperty(attr, config[attr]);
15100 // when size is auto or % webkit starts from zero instead of computed
15101 // (https://bugs.webkit.org/show_bug.cgi?id=16020)
15102 // TODO: selective set
15103 if (node.style[attr] === '') {
15104 Y.DOM.setStyle(node, attr, Y.DOM.getComputedStyle(node, attr));
15111 * Starts or an animation.
15116 run: function(callback) {
15119 config = anim._config,
15121 type: 'transition:start',
15126 if (!anim._running) {
15127 anim._running = true;
15129 //anim._node.fire('transition:start', data);
15131 if (config.on && config.on.start) {
15132 config.on.start.call(Y.one(node), data);
15135 anim.initAttrs(anim._config);
15137 anim._callback = callback;
15145 _start: function() {
15149 _prepDur: function(dur) {
15150 dur = parseFloat(dur);
15155 _runNative: function(time) {
15158 uid = Y.stamp(node),
15159 style = node.style,
15160 computed = getComputedStyle(node),
15161 attrs = Transition._nodeAttrs[uid],
15163 cssTransition = computed[TRANSITION_PROPERTY],
15165 transitionText = TRANSITION_PROPERTY + ': ',
15166 duration = TRANSITION_DURATION + ': ',
15167 easing = TRANSITION_TIMING_FUNCTION + ': ',
15168 delay = TRANSITION_DELAY + ': ',
15173 // preserve existing transitions
15174 if (cssTransition !== 'all') {
15175 transitionText += cssTransition + ',';
15176 duration += computed[TRANSITION_DURATION] + ',';
15177 easing += computed[TRANSITION_TIMING_FUNCTION] + ',';
15178 delay += computed[TRANSITION_DELAY] + ',';
15182 // run transitions mapped to this instance
15183 for (name in attrs) {
15184 hyphy = Transition._toHyphen(name);
15185 attr = attrs[name];
15186 if (attrs.hasOwnProperty(name) && attr.transition === anim) {
15187 if (name in node.style) { // only native styles allowed
15188 duration += anim._prepDur(attr.duration) + ',';
15189 delay += anim._prepDur(attr.delay) + ',';
15190 easing += (attr.easing) + ',';
15192 transitionText += hyphy + ',';
15193 cssText += hyphy + ': ' + attr.value + '; ';
15195 this.removeProperty(name);
15200 transitionText = transitionText.replace(/,$/, ';');
15201 duration = duration.replace(/,$/, ';');
15202 easing = easing.replace(/,$/, ';');
15203 delay = delay.replace(/,$/, ';');
15205 // only one native end event per node
15206 if (!Transition._hasEnd[uid]) {
15207 //anim._detach = Y.on(TRANSITION_END, anim._onNativeEnd, node);
15208 //node[ON_TRANSITION_END] = anim._onNativeEnd;
15209 node.addEventListener(TRANSITION_END, anim._onNativeEnd, false);
15210 Transition._hasEnd[uid] = true;
15214 //setTimeout(function() { // allow updates to apply (size fix, onstart, etc)
15215 style.cssText += transitionText + duration + easing + delay + cssText;
15220 _end: function(elapsed) {
15223 callback = anim._callback,
15224 config = anim._config,
15226 type: 'transition:end',
15228 elapsedTime: elapsed
15231 nodeInstance = Y.one(node);
15233 anim._running = false;
15234 anim._callback = null;
15237 if (config.on && config.on.end) {
15238 setTimeout(function() { // IE: allow previous update to finish
15239 config.on.end.call(nodeInstance, data);
15241 // nested to ensure proper fire order
15243 callback.call(nodeInstance, data);
15247 } else if (callback) {
15248 setTimeout(function() { // IE: allow previous update to finish
15249 callback.call(nodeInstance, data);
15252 //node.fire('transition:end', data);
15257 _endNative: function(name) {
15258 var node = this._node,
15259 value = node.ownerDocument.defaultView.getComputedStyle(node, '')[TRANSITION_PROPERTY];
15261 if (typeof value === 'string') {
15262 value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ',');
15263 value = value.replace(/^,|,$/, '');
15264 node.style[TRANSITION_CAMEL] = value;
15268 _onNativeEnd: function(e) {
15270 uid = Y.stamp(node),
15271 event = e,//e._event,
15272 name = Transition._toCamel(event.propertyName),
15273 elapsed = event.elapsedTime,
15274 attrs = Transition._nodeAttrs[uid],
15275 attr = attrs[name],
15276 anim = (attr) ? attr.transition : null,
15281 anim.removeProperty(name);
15282 anim._endNative(name);
15283 config = anim._config[name];
15286 type: 'propertyEnd',
15287 propertyName: name,
15288 elapsedTime: elapsed,
15292 if (config && config.on && config.on.end) {
15293 config.on.end.call(Y.one(node), data);
15296 //node.fire('transition:propertyEnd', data);
15298 if (anim._count <= 0) { // after propertyEnd fires
15299 anim._end(elapsed);
15304 destroy: function() {
15307 if (anim._detach) {
15308 anim._detach.detach();
15311 //anim._node[ON_TRANSITION_END] = null;
15312 node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
15317 Y.Transition = Transition;
15318 Y.TransitionNative = Transition; // TODO: remove
15321 * Animate one or more css properties to a given value. Requires the "transition" module.
15322 * <pre>example usage:
15323 * Y.one('#demo').transition({
15324 * duration: 1, // in seconds, default is 0.5
15325 * easing: 'ease-out', // default is 'ease'
15326 * delay: '1', // delay start for 1 second, default is 0
15331 * opacity: { // per property
15335 * easing: 'ease-in'
15340 * @method transition
15341 * @param {Object} config An object containing one or more style properties, a duration and an easing.
15342 * @param {Function} callback A function to run after the transition has completed.
15345 Y.Node.prototype.transition = function(name, config, callback) {
15347 transitionAttrs = Transition._nodeAttrs[Y.stamp(this._node)],
15348 anim = (transitionAttrs) ? transitionAttrs.transition || null : null,
15352 if (typeof name === 'string') { // named effect, pull config from registry
15353 if (typeof config === 'function') {
15358 fxConfig = Transition.fx[name];
15360 if (config && typeof config !== 'boolean') {
15361 config = Y.clone(config);
15363 for (prop in fxConfig) {
15364 if (fxConfig.hasOwnProperty(prop)) {
15365 if (! (prop in config)) {
15366 config[prop] = fxConfig[prop];
15374 } else { // name is a config, config is a callback or undefined
15379 if (anim && !anim._running) {
15380 anim.init(this, config);
15382 anim = new Transition(this._node, config);
15385 anim.run(callback);
15389 Y.Node.prototype.show = function(name, config, callback) {
15390 this._show(); // show prior to transition
15391 if (name && Y.Transition) {
15392 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default
15393 if (typeof config === 'function') {
15397 name = this.SHOW_TRANSITION;
15399 this.transition(name, config, callback);
15404 var _wrapCallBack = function(anim, fn, callback) {
15405 return function() {
15410 callback.apply(anim._node, arguments);
15415 Y.Node.prototype.hide = function(name, config, callback) {
15416 if (name && Y.Transition) {
15417 if (typeof config === 'function') {
15422 callback = _wrapCallBack(this, this._hide, callback); // wrap with existing callback
15423 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default
15424 if (typeof config === 'function') {
15428 name = this.HIDE_TRANSITION;
15430 this.transition(name, config, callback);
15438 * Animate one or more css properties to a given value. Requires the "transition" module.
15439 * <pre>example usage:
15440 * Y.all('.demo').transition({
15441 * duration: 1, // in seconds, default is 0.5
15442 * easing: 'ease-out', // default is 'ease'
15443 * delay: '1', // delay start for 1 second, default is 0
15448 * opacity: { // per property
15452 * easing: 'ease-in'
15457 * @method transition
15458 * @param {Object} config An object containing one or more style properties, a duration and an easing.
15459 * @param {Function} callback A function to run after the transition has completed. The callback fires
15460 * once per item in the NodeList.
15463 Y.NodeList.prototype.transition = function(config, callback) {
15464 var nodes = this._nodes,
15468 while ((node = nodes[i++])) {
15469 Y.one(node).transition(config, callback);
15475 Y.Node.prototype.toggleView = function(name, on) {
15477 this._toggles = this._toggles || [];
15479 if (typeof name == 'boolean') { // no transition, just toggle
15482 if (typeof on === 'undefined' && name in this._toggles) {
15483 on = ! this._toggles[name];
15491 callback = _wrapCallBack(anim, this._hide);
15494 this._toggles[name] = on;
15495 this.transition(Y.Transition.toggles[name][on], callback);
15498 Y.NodeList.prototype.toggleView = function(config, callback) {
15499 var nodes = this._nodes,
15503 while ((node = nodes[i++])) {
15504 Y.one(node).toggleView(config, callback);
15510 Y.mix(Transition.fx, {
15531 height: function(node) {
15532 return node.get('scrollHeight') + 'px';
15534 width: function(node) {
15535 return node.get('scrollWidth') + 'px';
15541 start: function() {
15542 var overflow = this.getStyle('overflow');
15543 if (overflow !== 'hidden') { // enable scrollHeight/Width
15544 this.setStyle('overflow', 'hidden');
15545 this._transitionOverflow = overflow;
15550 if (this._transitionOverflow) { // revert overridden value
15551 this.setStyle('overflow', this._transitionOverflow);
15558 Y.mix(Transition.toggles, {
15559 size: ['sizeIn', 'sizeOut'],
15560 fade: ['fadeOut', 'fadeIn']
15564 }, '3.3.0' ,{requires:['node-base']});
15565 YUI.add('transition-timer', function(Y) {
15568 * The Transition Utility provides an API for creating advanced transitions.
15569 * @module transition
15573 * Provides the base Transition class, for animating numeric properties.
15575 * @module transition
15576 * @submodule transition-timer
15580 var Transition = Y.Transition;
15582 Y.mix(Transition.prototype, {
15583 _start: function() {
15584 if (Transition.useNative) {
15591 _runTimer: function() {
15595 Transition._running[Y.stamp(anim)] = anim;
15596 anim._startTime = new Date();
15597 Transition._startTimer();
15600 _endTimer: function() {
15602 delete Transition._running[Y.stamp(anim)];
15603 anim._startTime = null;
15606 _runFrame: function() {
15607 var t = new Date() - this._startTime;
15611 _runAttrs: function(time) {
15614 config = anim._config,
15615 uid = Y.stamp(node),
15616 attrs = Transition._nodeAttrs[uid],
15617 customAttr = Transition.behaviors,
15630 for (name in attrs) {
15631 attribute = attrs[name];
15632 if ((attribute && attribute.transition === anim)) {
15633 d = attribute.duration;
15634 delay = attribute.delay;
15635 elapsed = (time - delay) / 1000;
15638 type: 'propertyEnd',
15639 propertyName: name,
15641 elapsedTime: elapsed
15644 setter = (i in customAttr && 'set' in customAttr[i]) ?
15645 customAttr[i].set : Transition.DEFAULT_SETTER;
15653 if (!delay || time >= delay) {
15654 setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
15655 attribute.easing, attribute.unit);
15658 delete attrs[name];
15661 if (config[name] && config[name].on && config[name].on.end) {
15662 config[name].on.end.call(Y.one(node), data);
15665 //node.fire('transition:propertyEnd', data);
15667 if (!allDone && anim._count <= 0) {
15669 anim._end(elapsed);
15679 _initAttrs: function() {
15681 customAttr = Transition.behaviors,
15682 uid = Y.stamp(anim._node),
15683 attrs = Transition._nodeAttrs[uid],
15694 for (name in attrs) {
15695 attribute = attrs[name];
15696 if (attrs.hasOwnProperty(name) && (attribute && attribute.transition === anim)) {
15697 duration = attribute.duration * 1000;
15698 delay = attribute.delay * 1000;
15699 easing = attribute.easing;
15700 val = attribute.value;
15702 // only allow supported properties
15703 if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
15704 begin = (name in customAttr && 'get' in customAttr[name]) ?
15705 customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
15707 mFrom = Transition.RE_UNITS.exec(begin);
15708 mTo = Transition.RE_UNITS.exec(val);
15710 begin = mFrom ? mFrom[1] : begin;
15711 end = mTo ? mTo[1] : val;
15712 unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
15714 if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
15715 unit = Transition.DEFAULT_UNIT;
15718 if (typeof easing === 'string') {
15719 if (easing.indexOf('cubic-bezier') > -1) {
15720 easing = easing.substring(13, easing.length - 1).split(',');
15721 } else if (Transition.easings[easing]) {
15722 easing = Transition.easings[easing];
15726 attribute.from = Number(begin);
15727 attribute.to = Number(end);
15728 attribute.unit = unit;
15729 attribute.easing = easing;
15730 attribute.duration = duration + delay;
15731 attribute.delay = delay;
15733 delete attrs[name];
15740 destroy: function() {
15746 Y.mix(Y.Transition, {
15749 * Regex of properties that should use the default unit.
15751 * @property RE_DEFAULT_UNIT
15754 RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
15757 * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
15759 * @property DEFAULT_UNIT
15762 DEFAULT_UNIT: 'px',
15765 * Time in milliseconds passed to setInterval for frame processing
15767 * @property intervalTime
15774 * Bucket for custom getters and setters
15776 * @property behaviors
15781 get: function(anim, attr) {
15782 return Y.DOM._getAttrOffset(anim._node, attr);
15788 * The default setter to use when setting object properties.
15790 * @property DEFAULT_SETTER
15793 DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
15794 from = Number(from);
15797 var node = anim._node,
15798 val = Transition.cubicBezier(fn, elapsed / duration);
15800 val = from + val[0] * (to - from);
15803 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
15805 Y.DOM.setStyle(node, att, val + unit);
15813 * The default getter to use when getting object properties.
15815 * @property DEFAULT_GETTER
15818 DEFAULT_GETTER: function(anim, att) {
15819 var node = anim._node,
15822 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
15823 val = Y.DOM.getComputedStyle(node, att);
15829 _startTimer: function() {
15830 if (!Transition._timer) {
15831 Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
15835 _stopTimer: function() {
15836 clearInterval(Transition._timer);
15837 Transition._timer = null;
15841 * Called per Interval to handle each animation frame.
15842 * @method _runFrame
15846 _runFrame: function() {
15849 for (anim in Transition._running) {
15850 if (Transition._running[anim]._runFrame) {
15852 Transition._running[anim]._runFrame();
15857 Transition._stopTimer();
15861 cubicBezier: function(p, t) {
15871 A = x3 - 3 * x2 + 3 * x1 - x0,
15872 B = 3 * x2 - 6 * x1 + 3 * x0,
15873 C = 3 * x1 - 3 * x0,
15875 E = y3 - 3 * y2 + 3 * y1 - y0,
15876 F = 3 * y2 - 6 * y1 + 3 * y0,
15877 G = 3 * y1 - 3 * y0,
15880 x = (((A*t) + B)*t + C)*t + D,
15881 y = (((E*t) + F)*t + G)*t + H;
15887 ease: [0.25, 0, 1, 0.25],
15888 linear: [0, 0, 1, 1],
15889 'ease-in': [0.42, 0, 1, 1],
15890 'ease-out': [0, 0, 0.58, 1],
15891 'ease-in-out': [0.42, 0, 0.58, 1]
15897 RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
15900 Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
15902 Y.Transition = Transition;
15905 }, '3.3.0' ,{requires:['transition-native', 'node-style']});
15908 YUI.add('transition', function(Y){}, '3.3.0' ,{use:['transition-native', 'transition-timer']});
15910 YUI.add('selector-css3', function(Y) {
15913 * The selector css3 module provides support for css3 selectors.
15915 * @submodule selector-css3
15920 an+b = get every _a_th node starting at the _b_th
15921 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
15922 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
15923 an+0 = get every _a_th element, "0" may be omitted
15926 Y.Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
15928 Y.Selector._getNth = function(node, expr, tag, reverse) {
15929 Y.Selector._reNth.test(expr);
15930 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
15931 n = RegExp.$2, // "n"
15932 oddeven = RegExp.$3, // "odd" or "even"
15933 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
15935 siblings = Y.Selector._children(node.parentNode, tag),
15939 a = 2; // always every other
15942 b = (oddeven === 'odd') ? 1 : 0;
15943 } else if ( isNaN(a) ) {
15944 a = (n) ? 1 : 0; // start from the first or no repeat
15947 if (a === 0) { // just the first
15949 b = siblings.length - b + 1;
15952 if (siblings[b - 1] === node) {
15958 } else if (a < 0) {
15959 reverse = !!reverse;
15964 for (var i = b - 1, len = siblings.length; i < len; i += a) {
15965 if ( i >= 0 && siblings[i] === node ) {
15970 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
15971 if ( i < len && siblings[i] === node ) {
15979 Y.mix(Y.Selector.pseudos, {
15980 'root': function(node) {
15981 return node === node.ownerDocument.documentElement;
15984 'nth-child': function(node, expr) {
15985 return Y.Selector._getNth(node, expr);
15988 'nth-last-child': function(node, expr) {
15989 return Y.Selector._getNth(node, expr, null, true);
15992 'nth-of-type': function(node, expr) {
15993 return Y.Selector._getNth(node, expr, node.tagName);
15996 'nth-last-of-type': function(node, expr) {
15997 return Y.Selector._getNth(node, expr, node.tagName, true);
16000 'last-child': function(node) {
16001 var children = Y.Selector._children(node.parentNode);
16002 return children[children.length - 1] === node;
16005 'first-of-type': function(node) {
16006 return Y.Selector._children(node.parentNode, node.tagName)[0] === node;
16009 'last-of-type': function(node) {
16010 var children = Y.Selector._children(node.parentNode, node.tagName);
16011 return children[children.length - 1] === node;
16014 'only-child': function(node) {
16015 var children = Y.Selector._children(node.parentNode);
16016 return children.length === 1 && children[0] === node;
16019 'only-of-type': function(node) {
16020 var children = Y.Selector._children(node.parentNode, node.tagName);
16021 return children.length === 1 && children[0] === node;
16024 'empty': function(node) {
16025 return node.childNodes.length === 0;
16028 'not': function(node, expr) {
16029 return !Y.Selector.test(node, expr);
16032 'contains': function(node, expr) {
16033 var text = node.innerText || node.textContent || '';
16034 return text.indexOf(expr) > -1;
16037 'checked': function(node) {
16038 return (node.checked === true || node.selected === true);
16041 enabled: function(node) {
16042 return (node.disabled !== undefined && !node.disabled);
16045 disabled: function(node) {
16046 return (node.disabled);
16050 Y.mix(Y.Selector.operators, {
16051 '^=': '^{val}', // Match starts with value
16052 '$=': '{val}$', // Match ends with value
16053 '*=': '{val}' // Match contains value as substring
16056 Y.Selector.combinators['~'] = {
16057 axis: 'previousSibling'
16061 }, '3.3.0' ,{requires:['dom-base', 'selector-native', 'selector-css2']});
16062 YUI.add('dom-style-ie', function(Y) {
16065 var HAS_LAYOUT = 'hasLayout',
16068 FILTERS = 'filters',
16069 OPACITY = 'opacity',
16072 BORDER_WIDTH = 'borderWidth',
16073 BORDER_TOP_WIDTH = 'borderTopWidth',
16074 BORDER_RIGHT_WIDTH = 'borderRightWidth',
16075 BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
16076 BORDER_LEFT_WIDTH = 'borderLeftWidth',
16079 TRANSPARENT = 'transparent',
16080 VISIBLE = 'visible',
16081 GET_COMPUTED_STYLE = 'getComputedStyle',
16082 UNDEFINED = undefined,
16083 documentElement = Y.config.doc.documentElement,
16085 testFeature = Y.Features.test,
16086 addFeature = Y.Features.add,
16088 // TODO: unit-less lineHeight (e.g. 1.22)
16089 re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
16091 isIE8 = (Y.UA.ie >= 8),
16093 _getStyleObj = function(node) {
16094 return node.currentStyle || node.style;
16100 get: function(el, property) {
16105 current = _getStyleObj(el)[property];
16107 if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
16108 value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
16109 } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
16111 } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
16112 value = Y.DOM.IE.COMPUTED[property](el, property);
16113 } else if (re_unit.test(current)) { // convert to pixel
16114 value = ComputedStyle.getPixel(el, property) + PX;
16124 width: ['Left', 'Right'],
16125 height: ['Top', 'Bottom'],
16130 getOffset: function(el, prop) {
16131 var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
16132 capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
16133 offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
16134 pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
16135 sizeOffsets = ComputedStyle.sizeOffsets[prop],
16136 mode = el.ownerDocument.compatMode,
16139 // IE pixelWidth incorrect for percent
16140 // manually compute by subtracting padding and border from offset size
16141 // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
16142 // reverting to auto from auto causes position stacking issues (old impl)
16143 if (current === AUTO || current.indexOf('%') > -1) {
16144 value = el['offset' + capped];
16146 if (mode !== 'BackCompat') {
16147 if (sizeOffsets[0]) {
16148 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
16149 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
16152 if (sizeOffsets[1]) {
16153 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
16154 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
16158 } else { // use style.pixelWidth, etc. to convert to pixels
16159 // need to map style.width to currentStyle (no currentStyle.pixelWidth)
16160 if (!el.style[pixel] && !el.style[prop]) {
16161 el.style[prop] = current;
16163 value = el.style[pixel];
16170 thin: (isIE8) ? '1px' : '2px',
16171 medium: (isIE8) ? '3px': '4px',
16172 thick: (isIE8) ? '5px' : '6px'
16175 getBorderWidth: function(el, property, omitUnit) {
16176 var unit = omitUnit ? '' : PX,
16177 current = el.currentStyle[property];
16179 if (current.indexOf(PX) < 0) { // look up keywords if a border exists
16180 if (ComputedStyle.borderMap[current] &&
16181 el.currentStyle.borderStyle !== 'none') {
16182 current = ComputedStyle.borderMap[current];
16183 } else { // otherwise no border (default is "medium")
16187 return (omitUnit) ? parseFloat(current) : current;
16190 getPixel: function(node, att) {
16191 // use pixelRight to convert to px
16193 style = _getStyleObj(node),
16194 styleRight = style.right,
16195 current = style[att];
16197 node.style.right = current;
16198 val = node.style.pixelRight;
16199 node.style.right = styleRight; // revert
16204 getMargin: function(node, att) {
16206 style = _getStyleObj(node);
16208 if (style[att] == AUTO) {
16211 val = ComputedStyle.getPixel(node, att);
16216 getVisibility: function(node, att) {
16218 while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
16219 node = node.parentNode;
16221 return (current) ? current[att] : VISIBLE;
16224 getColor: function(node, att) {
16225 var current = _getStyleObj(node)[att];
16227 if (!current || current === TRANSPARENT) {
16228 Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
16229 current = _getStyleObj(parent)[att];
16230 if (current && current !== TRANSPARENT) {
16237 return Y.Color.toRGB(current);
16240 getBorderColor: function(node, att) {
16241 var current = _getStyleObj(node),
16242 val = current[att] || current.color;
16243 return Y.Color.toRGB(Y.Color.toHex(val));
16247 //fontSize: getPixelFont,
16250 addFeature('style', 'computedStyle', {
16252 return 'getComputedStyle' in Y.config.win;
16256 addFeature('style', 'opacity', {
16258 return 'opacity' in documentElement.style;
16262 addFeature('style', 'filter', {
16264 return 'filters' in documentElement;
16268 // use alpha filter for IE opacity
16269 if (!testFeature('style', 'opacity') && testFeature('style', 'filter')) {
16270 Y.DOM.CUSTOM_STYLES[OPACITY] = {
16271 get: function(node) {
16273 try { // will error if no DXImageTransform
16274 val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
16277 try { // make sure its in the document
16278 val = node[FILTERS]('alpha')[OPACITY];
16285 set: function(node, val, style) {
16287 styleObj = _getStyleObj(node),
16288 currentFilter = styleObj[FILTER];
16290 style = style || node.style;
16291 if (val === '') { // normalize inline style behavior
16292 current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
16296 if (typeof currentFilter == 'string') { // in case not appended
16297 style[FILTER] = currentFilter.replace(/alpha([^)]*\))/gi, '') +
16298 ((val < 1) ? 'alpha(' + OPACITY + '=' + val * 100 + ')' : '');
16300 if (!style[FILTER]) {
16301 style.removeAttribute(FILTER);
16304 if (!styleObj[HAS_LAYOUT]) {
16305 style.zoom = 1; // needs layout
16313 Y.config.doc.createElement('div').style.height = '-1px';
16314 } catch(e) { // IE throws error on invalid style set; trap common cases
16315 Y.DOM.CUSTOM_STYLES.height = {
16316 set: function(node, val, style) {
16317 var floatVal = parseFloat(val);
16318 if (floatVal >= 0 || val === 'auto' || val === '') {
16319 style.height = val;
16325 Y.DOM.CUSTOM_STYLES.width = {
16326 set: function(node, val, style) {
16327 var floatVal = parseFloat(val);
16328 if (floatVal >= 0 || val === 'auto' || val === '') {
16336 if (!testFeature('style', 'computedStyle')) {
16337 // TODO: top, right, bottom, left
16338 IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
16340 IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
16342 IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
16343 IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
16344 ComputedStyle.getBorderWidth;
16346 IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
16347 IEComputed.marginLeft = ComputedStyle.getMargin;
16349 IEComputed.visibility = ComputedStyle.getVisibility;
16350 IEComputed.borderColor = IEComputed.borderTopColor =
16351 IEComputed.borderRightColor = IEComputed.borderBottomColor =
16352 IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
16354 Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
16356 Y.namespace('DOM.IE');
16357 Y.DOM.IE.COMPUTED = IEComputed;
16358 Y.DOM.IE.ComputedStyle = ComputedStyle;
16364 }, '3.3.0' ,{requires:['dom-style']});
16365 YUI.add('simpleyui', function(Y) {
16371 }, '3.3.0' ,{use:['yui','oop','dom','event-custom-base','event-base','pluginhost','node','event-delegate','io-base','json-parse','transition','selector-css3','dom-style-ie','querystring-stringify-simple']});
16372 var Y = YUI().use('*');