/* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-core', function(Y) { /** * The State class maintains state for a collection of named items, with * a varying number of properties defined. * * It avoids the need to create a separate class for the item, and separate instances * of these classes for each item, by storing the state in a 2 level hash table, * improving performance when the number of items is likely to be large. * * @constructor * @class State */ Y.State = function() { /** * Hash of attributes * @property data */ this.data = {}; }; Y.State.prototype = { /** * Adds a property to an item. * * @method add * @param name {String} The name of the item. * @param key {String} The name of the property. * @param val {Any} The value of the property. */ add : function(name, key, val) { var d = this.data; d[name] = d[name] || {}; d[name][key] = val; }, /** * Adds multiple properties to an item. * * @method addAll * @param name {String} The name of the item. * @param o {Object} A hash of property/value pairs. */ addAll: function(name, o) { var key; for (key in o) { if (o.hasOwnProperty(key)) { this.add(name, key, o[key]); } } }, /** * Removes a property from an item. * * @method remove * @param name {String} The name of the item. * @param key {String} The property to remove. */ remove: function(name, key) { var d = this.data; if (d[name]) { delete d[name][key]; } }, /** * Removes multiple properties from an item, or remove the item completely. * * @method removeAll * @param name {String} The name of the item. * @param o {Object|Array} Collection of properties to delete. If not provided, the entire item is removed. */ removeAll: function(name, o) { var d = this.data; if (!o) { if (d[name]) { delete d[name]; } } else { Y.each(o, function(v, k) { if(Y.Lang.isString(k)) { this.remove(name, k); } else { this.remove(name, v); } }, this); } }, /** * For a given item, returns the value of the property requested, or undefined if not found. * * @method get * @param name {String} The name of the item * @param key {String} Optional. The property value to retrieve. * @return {Any} The value of the supplied property. */ get: function(name, key) { var d = this.data; return (d[name]) ? d[name][key] : undefined; }, /** * For the given item, returns an object with all of the * item's property/value pairs. By default the object returned * is a shallow copy of the stored data, but passing in true * as the second parameter will return a reference to the stored * data. * * @method getAll * @param name {String} The name of the item * @param reference {boolean} true, if you want a reference to the stored * object * @return {Object} An object with property/value pairs for the item. */ getAll : function(name, reference) { var d = this.data, o; if (!reference) { Y.each(d[name], function(v, k) { o = o || {}; o[k] = v; }); } else { o = d[name]; } return o; } }; /** * The attribute module provides an augmentable Attribute implementation, which * adds configurable attributes and attribute change events to the class being * augmented. It also provides a State class, which is used internally by Attribute, * but can also be used independently to provide a name/property/value data structure to * store state. * * @module attribute */ /** * The attribute-core submodule provides the lightest level of attribute handling support * without Attribute change events, or lesser used methods such as reset(), modifyAttrs(), * and removeAttr(). * * @module attribute * @submodule attribute-core */ var O = Y.Object, Lang = Y.Lang, DOT = ".", // Externally configurable props GETTER = "getter", SETTER = "setter", READ_ONLY = "readOnly", WRITE_ONCE = "writeOnce", INIT_ONLY = "initOnly", VALIDATOR = "validator", VALUE = "value", VALUE_FN = "valueFn", LAZY_ADD = "lazyAdd", // Used for internal state management ADDED = "added", BYPASS_PROXY = "_bypassProxy", INITIALIZING = "initializing", INIT_VALUE = "initValue", LAZY = "lazy", IS_LAZY_ADD = "isLazyAdd", INVALID_VALUE; /** *

* AttributeCore provides the lightest level of configurable attribute support. It is designed to be * augmented on to a host class, and provides the host with the ability to configure * attributes to store and retrieve state, but without support for attribute change events. *

*

For example, attributes added to the host can be configured:

* * *

See the addAttr method, for the complete set of configuration * options available for attributes.

* *

Object/Classes based on AttributeCore can augment AttributeEvents * (with true for overwrite) and AttributeExtras to add attribute event and * additional, less commonly used attribute methods, such as `modifyAttr`, `removeAttr` and `reset`.

* * @class AttributeCore * @param attrs {Object} The attributes to add during construction (passed through to addAttrs). These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. * @param values {Object} The initial attribute values to apply (passed through to addAttrs). These are not merged/cloned. The caller is responsible for isolating user provided values if required. * @param lazy {boolean} Whether or not to add attributes lazily (passed through to addAttrs). */ function AttributeCore(attrs, values, lazy) { this._initAttrHost(attrs, values, lazy); } /** *

The value to return from an attribute setter in order to prevent the set from going through.

* *

You can return this value from your setter if you wish to combine validator and setter * functionality into a single setter function, which either returns the massaged value to be stored or * AttributeCore.INVALID_VALUE to prevent invalid values from being stored.

* * @property INVALID_VALUE * @type Object * @static * @final */ AttributeCore.INVALID_VALUE = {}; INVALID_VALUE = AttributeCore.INVALID_VALUE; /** * The list of properties which can be configured for * each attribute (e.g. setter, getter, writeOnce etc.). * * This property is used internally as a whitelist for faster * Y.mix operations. * * @property _ATTR_CFG * @type Array * @static * @protected */ AttributeCore._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BYPASS_PROXY]; AttributeCore.prototype = { /** * Constructor logic for attributes. Initializes the host state, and sets up the inital attributes passed to the * constructor. * * @method _initAttrHost * @param attrs {Object} The attributes to add during construction (passed through to addAttrs). These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. * @param values {Object} The initial attribute values to apply (passed through to addAttrs). These are not merged/cloned. The caller is responsible for isolating user provided values if required. * @param lazy {boolean} Whether or not to add attributes lazily (passed through to addAttrs). * @private */ _initAttrHost : function(attrs, values, lazy) { this._state = new Y.State(); this._initAttrs(attrs, values, lazy); }, /** *

* Adds an attribute with the provided configuration to the host object. *

*

* The config argument object supports the following properties: *

* *
*
value <Any>
*
The initial value to set on the attribute
* *
valueFn <Function | String>
*
*

A function, which will return the initial value to set on the attribute. This is useful * for cases where the attribute configuration is defined statically, but needs to * reference the host instance ("this") to obtain an initial value. If both the value and valueFn properties are defined, * the value returned by the valueFn has precedence over the value property, unless it returns undefined, in which * case the value property is used.

* *

valueFn can also be set to a string, representing the name of the instance method to be used to retrieve the value.

*
* *
readOnly <boolean>
*
Whether or not the attribute is read only. Attributes having readOnly set to true * cannot be modified by invoking the set method.
* *
writeOnce <boolean> or <string>
*
* Whether or not the attribute is "write once". Attributes having writeOnce set to true, * can only have their values set once, be it through the default configuration, * constructor configuration arguments, or by invoking set. *

The writeOnce attribute can also be set to the string "initOnly", in which case the attribute can only be set during initialization * (when used with Base, this means it can only be set during construction)

*
* *
setter <Function | String>
*
*

The setter function used to massage or normalize the value passed to the set method for the attribute. * The value returned by the setter will be the final stored value. Returning * Attribute.INVALID_VALUE, from the setter will prevent * the value from being stored. *

* *

setter can also be set to a string, representing the name of the instance method to be used as the setter function.

*
* *
getter <Function | String>
*
*

* The getter function used to massage or normalize the value returned by the get method for the attribute. * The value returned by the getter function is the value which will be returned to the user when they * invoke get. *

* *

getter can also be set to a string, representing the name of the instance method to be used as the getter function.

*
* *
validator <Function | String>
*
*

* The validator function invoked prior to setting the stored value. Returning * false from the validator function will prevent the value from being stored. *

* *

validator can also be set to a string, representing the name of the instance method to be used as the validator function.

*
* *
lazyAdd <boolean>
*
Whether or not to delay initialization of the attribute until the first call to get/set it. * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through * the addAttrs method.
* *
* *

The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with * the context ("this") set to the host object.

* *

Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, * and are not intended for public use.

* * @method addAttr * * @param {String} name The name of the attribute. * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute. * *

* NOTE: The configuration object is modified when adding an attribute, so if you need * to protect the original values, you will need to merge the object. *

* * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set). * * @return {Object} A reference to the host object. * * @chainable */ addAttr: function(name, config, lazy) { var host = this, // help compression state = host._state, value, hasValue; config = config || {}; lazy = (LAZY_ADD in config) ? config[LAZY_ADD] : lazy; if (lazy && !host.attrAdded(name)) { state.addAll(name, { lazy : config, added : true }); } else { if (!host.attrAdded(name) || state.get(name, IS_LAZY_ADD)) { hasValue = (VALUE in config); if (hasValue) { // We'll go through set, don't want to set value in config directly value = config.value; delete config.value; } config.added = true; config.initializing = true; state.addAll(name, config); if (hasValue) { // Go through set, so that raw values get normalized/validated host.set(name, value); } state.remove(name, INITIALIZING); } } return host; }, /** * Checks if the given attribute has been added to the host * * @method attrAdded * @param {String} name The name of the attribute to check. * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. This method will return true for lazily added attributes. */ attrAdded: function(name) { return !!this._state.get(name, ADDED); }, /** * Returns the current value of the attribute. If the attribute * has been configured with a 'getter' function, this method will delegate * to the 'getter' to obtain the value of the attribute. * * @method get * * @param {String} name The name of the attribute. If the value of the attribute is an Object, * dot notation can be used to obtain the value of a property of the object (e.g. get("x.y.z")) * * @return {Any} The value of the attribute */ get : function(name) { return this._getAttr(name); }, /** * Checks whether or not the attribute is one which has been * added lazily and still requires initialization. * * @method _isLazyAttr * @private * @param {String} name The name of the attribute * @return {boolean} true if it's a lazily added attribute, false otherwise. */ _isLazyAttr: function(name) { return this._state.get(name, LAZY); }, /** * Finishes initializing an attribute which has been lazily added. * * @method _addLazyAttr * @private * @param {Object} name The name of the attribute */ _addLazyAttr: function(name, cfg) { var state = this._state, lazyCfg = state.get(name, LAZY); state.add(name, IS_LAZY_ADD, true); state.remove(name, LAZY); this.addAttr(name, lazyCfg); }, /** * Sets the value of an attribute. * * @method set * @chainable * * @param {String} name The name of the attribute. If the * current value of the attribute is an Object, dot notation can be used * to set the value of a property within the object (e.g. set("x.y.z", 5)). * * @param {Any} value The value to set the attribute to. * * @return {Object} A reference to the host object. */ set : function(name, val) { return this._setAttr(name, val); }, /** * Allows setting of readOnly/writeOnce attributes. See set for argument details. * * @method _set * @protected * @chainable * * @param {String} name The name of the attribute. * @param {Any} val The value to set the attribute to. * @return {Object} A reference to the host object. */ _set : function(name, val) { return this._setAttr(name, val, null, true); }, /** * Provides the common implementation for the public set and protected _set methods. * * See set for argument details. * * @method _setAttr * @protected * @chainable * * @param {String} name The name of the attribute. * @param {Any} value The value to set the attribute to. * @param {Object} opts (Optional) Optional event data to be mixed into * the event facade passed to subscribers of the attribute's change event. * This is currently a hack. There's no real need for the AttributeCore implementation * to support this parameter, but breaking it out into AttributeEvents, results in * additional function hops for the critical path. May change in 3.5.0 PR3. * @param {boolean} force If true, allows the caller to set values for * readOnly or writeOnce attributes which have already been set. * * @return {Object} A reference to the host object. */ _setAttr : function(name, val, opts, force) { // HACK - no real reason core needs to know about opts, but // it adds fn hops if we want to break it out. // Not sure it's worth it for this critical path var allowSet = true, state = this._state, stateProxy = this._stateProxy, cfg, initialSet, strPath, path, currVal, writeOnce, initializing; if (name.indexOf(DOT) !== -1) { strPath = name; path = name.split(DOT); name = path.shift(); } if (this._isLazyAttr(name)) { this._addLazyAttr(name); } cfg = state.getAll(name, true) || {}; initialSet = (!(VALUE in cfg)); if (stateProxy && name in stateProxy && !cfg._bypassProxy) { // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set? initialSet = false; } writeOnce = cfg.writeOnce; initializing = cfg.initializing; if (!initialSet && !force) { if (writeOnce) { allowSet = false; } if (cfg.readOnly) { allowSet = false; } } if (!initializing && !force && writeOnce === INIT_ONLY) { allowSet = false; } if (allowSet) { // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value) if (!initialSet) { currVal = this.get(name); } if (path) { val = O.setValue(Y.clone(currVal), path, val); if (val === undefined) { allowSet = false; } } if (allowSet) { if (!this._fireAttrChange || initializing) { this._setAttrVal(name, strPath, currVal, val); } else { // HACK - no real reason core needs to know about _fireAttrChange, but // it adds fn hops if we want to break it out. Not sure it's worth it for this critical path this._fireAttrChange(name, strPath, currVal, val, opts); } } } return this; }, /** * Provides the common implementation for the public get method, * allowing Attribute hosts to over-ride either method. * * See get for argument details. * * @method _getAttr * @protected * @chainable * * @param {String} name The name of the attribute. * @return {Any} The value of the attribute. */ _getAttr : function(name) { var host = this, // help compression fullName = name, state = host._state, path, getter, val, cfg; if (name.indexOf(DOT) !== -1) { path = name.split(DOT); name = path.shift(); } // On Demand - Should be rare - handles out of order valueFn references if (host._tCfgs && host._tCfgs[name]) { cfg = {}; cfg[name] = host._tCfgs[name]; delete host._tCfgs[name]; host._addAttrs(cfg, host._tVals); } // Lazy Init if (host._isLazyAttr(name)) { host._addLazyAttr(name); } val = host._getStateVal(name); getter = state.get(name, GETTER); if (getter && !getter.call) { getter = this[getter]; } val = (getter) ? getter.call(host, val, fullName) : val; val = (path) ? O.getValue(val, path) : val; return val; }, /** * Gets the stored value for the attribute, from either the * internal state object, or the state proxy if it exits * * @method _getStateVal * @private * @param {String} name The name of the attribute * @return {Any} The stored value of the attribute */ _getStateVal : function(name) { var stateProxy = this._stateProxy; return stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY) ? stateProxy[name] : this._state.get(name, VALUE); }, /** * Sets the stored value for the attribute, in either the * internal state object, or the state proxy if it exits * * @method _setStateVal * @private * @param {String} name The name of the attribute * @param {Any} value The value of the attribute */ _setStateVal : function(name, value) { var stateProxy = this._stateProxy; if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) { stateProxy[name] = value; } else { this._state.add(name, VALUE, value); } }, /** * Updates the stored value of the attribute in the privately held State object, * if validation and setter passes. * * @method _setAttrVal * @private * @param {String} attrName The attribute name. * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z"). * @param {Any} prevVal The currently stored value of the attribute. * @param {Any} newVal The value which is going to be stored. * * @return {booolean} true if the new attribute value was stored, false if not. */ _setAttrVal : function(attrName, subAttrName, prevVal, newVal) { var host = this, allowSet = true, cfg = this._state.getAll(attrName, true) || {}, validator = cfg.validator, setter = cfg.setter, initializing = cfg.initializing, prevRawVal = this._getStateVal(attrName), name = subAttrName || attrName, retVal, valid; if (validator) { if (!validator.call) { // Assume string - trying to keep critical path tight, so avoiding Lang check validator = this[validator]; } if (validator) { valid = validator.call(host, newVal, name); if (!valid && initializing) { newVal = cfg.defaultValue; valid = true; // Assume it's valid, for perf. } } } if (!validator || valid) { if (setter) { if (!setter.call) { // Assume string - trying to keep critical path tight, so avoiding Lang check setter = this[setter]; } if (setter) { retVal = setter.call(host, newVal, name); if (retVal === INVALID_VALUE) { allowSet = false; } else if (retVal !== undefined){ newVal = retVal; } } } if (allowSet) { if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) { allowSet = false; } else { // Store value if (!(INIT_VALUE in cfg)) { cfg.initValue = newVal; } host._setStateVal(attrName, newVal); } } } else { allowSet = false; } return allowSet; }, /** * Sets multiple attribute values. * * @method setAttrs * @param {Object} attrs An object with attributes name/value pairs. * @return {Object} A reference to the host object. * @chainable */ setAttrs : function(attrs) { return this._setAttrs(attrs); }, /** * Implementation behind the public setAttrs method, to set multiple attribute values. * * @method _setAttrs * @protected * @param {Object} attrs An object with attributes name/value pairs. * @return {Object} A reference to the host object. * @chainable */ _setAttrs : function(attrs) { for (var attr in attrs) { if ( attrs.hasOwnProperty(attr) ) { this.set(attr, attrs[attr]); } } return this; }, /** * Gets multiple attribute values. * * @method getAttrs * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are * returned. If set to true, all attributes modified from their initial values are returned. * @return {Object} An object with attribute name/value pairs. */ getAttrs : function(attrs) { return this._getAttrs(attrs); }, /** * Implementation behind the public getAttrs method, to get multiple attribute values. * * @method _getAttrs * @protected * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are * returned. If set to true, all attributes modified from their initial values are returned. * @return {Object} An object with attribute name/value pairs. */ _getAttrs : function(attrs) { var host = this, o = {}, i, l, attr, val, modifiedOnly = (attrs === true); // TODO - figure out how to get all "added" attrs = (attrs && !modifiedOnly) ? attrs : O.keys(host._state.data); for (i = 0, l = attrs.length; i < l; i++) { // Go through get, to honor cloning/normalization attr = attrs[i]; val = host.get(attr); if (!modifiedOnly || host._getStateVal(attr) != host._state.get(attr, INIT_VALUE)) { o[attr] = host.get(attr); } } return o; }, /** * Configures a group of attributes, and sets initial values. * *

* NOTE: This method does not isolate the configuration object by merging/cloning. * The caller is responsible for merging/cloning the configuration object if required. *

* * @method addAttrs * @chainable * * @param {Object} cfgs An object with attribute name/configuration pairs. * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply. * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only. * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set. * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration. * See addAttr. * * @return {Object} A reference to the host object. */ addAttrs : function(cfgs, values, lazy) { var host = this; // help compression if (cfgs) { host._tCfgs = cfgs; host._tVals = host._normAttrVals(values); host._addAttrs(cfgs, host._tVals, lazy); host._tCfgs = host._tVals = null; } return host; }, /** * Implementation behind the public addAttrs method. * * This method is invoked directly by get if it encounters a scenario * in which an attribute's valueFn attempts to obtain the * value an attribute in the same group of attributes, which has not yet * been added (on demand initialization). * * @method _addAttrs * @private * @param {Object} cfgs An object with attribute name/configuration pairs. * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply. * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only. * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set. * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration. * See addAttr. */ _addAttrs : function(cfgs, values, lazy) { var host = this, // help compression attr, attrCfg, value; for (attr in cfgs) { if (cfgs.hasOwnProperty(attr)) { // Not Merging. Caller is responsible for isolating configs attrCfg = cfgs[attr]; attrCfg.defaultValue = attrCfg.value; // Handle simple, complex and user values, accounting for read-only value = host._getAttrInitVal(attr, attrCfg, host._tVals); if (value !== undefined) { attrCfg.value = value; } if (host._tCfgs[attr]) { delete host._tCfgs[attr]; } host.addAttr(attr, attrCfg, lazy); } } }, /** * Utility method to protect an attribute configuration * hash, by merging the entire object and the individual * attr config objects. * * @method _protectAttrs * @protected * @param {Object} attrs A hash of attribute to configuration object pairs. * @return {Object} A protected version of the attrs argument. */ _protectAttrs : function(attrs) { if (attrs) { attrs = Y.merge(attrs); for (var attr in attrs) { if (attrs.hasOwnProperty(attr)) { attrs[attr] = Y.merge(attrs[attr]); } } } return attrs; }, /** * Utility method to normalize attribute values. The base implementation * simply merges the hash to protect the original. * * @method _normAttrVals * @param {Object} valueHash An object with attribute name/value pairs * * @return {Object} * * @private */ _normAttrVals : function(valueHash) { return (valueHash) ? Y.merge(valueHash) : null; }, /** * Returns the initial value of the given attribute from * either the default configuration provided, or the * over-ridden value if it exists in the set of initValues * provided and the attribute is not read-only. * * @param {String} attr The name of the attribute * @param {Object} cfg The attribute configuration object * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals * * @return {Any} The initial value of the attribute. * * @method _getAttrInitVal * @private */ _getAttrInitVal : function(attr, cfg, initValues) { var val, valFn; // init value is provided by the user if it exists, else, provided by the config if (!cfg.readOnly && initValues && initValues.hasOwnProperty(attr)) { val = initValues[attr]; } else { val = cfg.value; valFn = cfg.valueFn; if (valFn) { if (!valFn.call) { valFn = this[valFn]; } if (valFn) { val = valFn.call(this, attr); } } } return val; }, /** * Utility method to set up initial attributes defined during construction, either through the constructor.ATTRS property, or explicitly passed in. * * @method _initAttrs * @protected * @param attrs {Object} The attributes to add during construction (passed through to addAttrs). These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. * @param values {Object} The initial attribute values to apply (passed through to addAttrs). These are not merged/cloned. The caller is responsible for isolating user provided values if required. * @param lazy {boolean} Whether or not to add attributes lazily (passed through to addAttrs). */ _initAttrs : function(attrs, values, lazy) { // ATTRS support for Node, which is not Base based attrs = attrs || this.constructor.ATTRS; var Base = Y.Base, BaseCore = Y.BaseCore, baseInst = (Base && Y.instanceOf(this, Base)), baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore)); if ( attrs && !baseInst && !baseCoreInst) { this.addAttrs(this._protectAttrs(attrs), values, lazy); } } }; Y.AttributeCore = AttributeCore; }, '3.5.1' ); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('base-core', function(Y) { /** * The base module provides the Base class, which objects requiring attribute and custom event support can extend. * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides * plugin support and also provides the BaseCore.build method which provides a way to build custom classes using extensions. * * @module base */ /** *

The base-core module provides the BaseCore class, the lightest version of Base, * which provides Base's basic lifecycle management and ATTRS construction support, * but doesn't fire init/destroy or attribute change events.

* *

It mixes in AttributeCore, which is the lightest version of Attribute

* * @module base * @submodule base-core */ var O = Y.Object, L = Y.Lang, DOT = ".", INITIALIZED = "initialized", DESTROYED = "destroyed", INITIALIZER = "initializer", OBJECT_CONSTRUCTOR = Object.prototype.constructor, DEEP = "deep", SHALLOW = "shallow", DESTRUCTOR = "destructor", AttributeCore = Y.AttributeCore, _wlmix = function(r, s, wlhash) { var p; for (p in s) { if(wlhash[p]) { r[p] = s[p]; } } return r; }; /** * The BaseCore class, is the lightest version of Base, and provides Base's * basic lifecycle management and ATTRS construction support, but doesn't * fire init/destroy or attribute change events. * * BaseCore also handles the chaining of initializer and destructor methods across * the hierarchy as part of object construction and destruction. Additionally, attributes * configured through the static ATTRS * property for each class in the hierarchy will be initialized by BaseCore. * * Classes which require attribute support, but don't intend to use/expose attribute * change events can extend BaseCore instead of Base for optimal kweight and * runtime performance. * * @class BaseCore * @constructor * @uses AttributeCore * @param {Object} cfg Object with configuration property name/value pairs. * The object can be used to provide initial values for the objects published * attributes. */ function BaseCore(cfg) { if (!this._BaseInvoked) { this._BaseInvoked = true; this._initBase(cfg); } } /** * The list of properties which can be configured for each attribute * (e.g. setter, getter, writeOnce, readOnly etc.) * * @property _ATTR_CFG * @type Array * @static * @private */ BaseCore._ATTR_CFG = AttributeCore._ATTR_CFG.concat("cloneDefaultValue"); BaseCore._ATTR_CFG_HASH = Y.Array.hash(BaseCore._ATTR_CFG); /** * The array of non-attribute configuration properties supported by this class. * * For example `BaseCore` defines a "plugins" configuration property which * should not be set up as an attribute. This property is primarily required so * that when `_allowAdHocAttrs` is enabled by a class, * non-attribute configuration properties don't get added as ad-hoc attributes. * * @property _NON_ATTRS_CFG * @type Array * @static * @private */ BaseCore._NON_ATTRS_CFG = ["plugins"]; /** * This property controls whether or not instances of this class should * allow users to add ad-hoc attributes through the constructor configuration * hash. * * AdHoc attributes are attributes which are not defined by the class, and are * not handled by the MyClass._NON_ATTRS_CFG * * @property _allowAdHocAttrs * @type boolean * @default undefined (false) * @protected */ /** * The string to be used to identify instances of this class. * * Classes extending BaseCore, should define their own * static NAME property, which should be camelCase by * convention (e.g. MyClass.NAME = "myClass";). * * @property NAME * @type String * @static */ BaseCore.NAME = "baseCore"; /** * The default set of attributes which will be available for instances of this class, and * their configuration. In addition to the configuration properties listed by * AttributeCore's addAttr method, * the attribute can also be configured with a "cloneDefaultValue" property, which * defines how the statically defined value field should be protected * ("shallow", "deep" and false are supported values). * * By default if the value is an object literal or an array it will be "shallow" * cloned, to protect the default value. * * @property ATTRS * @type Object * @static */ BaseCore.ATTRS = { /** * Flag indicating whether or not this object * has been through the init lifecycle phase. * * @attribute initialized * @readonly * @default false * @type boolean */ initialized: { readOnly:true, value:false }, /** * Flag indicating whether or not this object * has been through the destroy lifecycle phase. * * @attribute destroyed * @readonly * @default false * @type boolean */ destroyed: { readOnly:true, value:false } }; BaseCore.prototype = { /** * Internal construction logic for BaseCore. * * @method _initBase * @param {Object} config The constructor configuration object * @private */ _initBase : function(config) { Y.stamp(this); this._initAttribute(config); // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's // initial state, but don't initialize Plugins yet. That's done after initialization. var PluginHost = Y.Plugin && Y.Plugin.Host; if (this._initPlugins && PluginHost) { PluginHost.call(this); } if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; } /** * The string used to identify the class of this object. * * @deprecated Use this.constructor.NAME * @property name * @type String */ this.name = this.constructor.NAME; this.init.apply(this, arguments); }, /** * Initializes AttributeCore * * @method _initAttribute * @private */ _initAttribute: function() { AttributeCore.apply(this); }, /** * Init lifecycle method, invoked during construction. Sets up attributes * and invokes initializers for the class hierarchy. * * @method init * @chainable * @param {Object} cfg Object with configuration property name/value pairs * @return {BaseCore} A reference to this object */ init: function(cfg) { this._baseInit(cfg); return this; }, /** * Internal initialization implementation for BaseCore * * @method _baseInit * @private */ _baseInit: function(cfg) { this._initHierarchy(cfg); if (this._initPlugins) { // Need to initPlugins manually, to handle constructor parsing, static Plug parsing this._initPlugins(cfg); } this._set(INITIALIZED, true); }, /** * Destroy lifecycle method. Invokes destructors for the class hierarchy. * * @method destroy * @return {BaseCore} A reference to this object * @chainable */ destroy: function() { this._baseDestroy(); return this; }, /** * Internal destroy implementation for BaseCore * * @method _baseDestroy * @private */ _baseDestroy : function() { if (this._destroyPlugins) { this._destroyPlugins(); } this._destroyHierarchy(); this._set(DESTROYED, true); }, /** * Returns the class hierarchy for this object, with BaseCore being the last class in the array. * * @method _getClasses * @protected * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object. * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the * cached value. */ _getClasses : function() { if (!this._classes) { this._initHierarchyData(); } return this._classes; }, /** * Returns an aggregated set of attribute configurations, by traversing * the class hierarchy. * * @method _getAttrCfgs * @protected * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return * the cached value. */ _getAttrCfgs : function() { if (!this._attrs) { this._initHierarchyData(); } return this._attrs; }, /** * A helper method used when processing ATTRS across the class hierarchy during * initialization. Returns a disposable object with the attributes defined for * the provided class, extracted from the set of all attributes passed in. * * @method _filterAttrCfs * @private * * @param {Function} clazz The class for which the desired attributes are required. * @param {Object} allCfgs The set of all attribute configurations for this instance. * Attributes will be removed from this set, if they belong to the filtered class, so * that by the time all classes are processed, allCfgs will be empty. * * @return {Object} The set of attributes belonging to the class passed in, in the form * of an object with attribute name/configuration pairs. */ _filterAttrCfgs : function(clazz, allCfgs) { var cfgs = null, attr, attrs = clazz.ATTRS; if (attrs) { for (attr in attrs) { if (allCfgs[attr]) { cfgs = cfgs || {}; cfgs[attr] = allCfgs[attr]; allCfgs[attr] = null; } } } return cfgs; }, /** * @method _filterAdHocAttrs * @private * * @param {Object} allAttrs The set of all attribute configurations for this instance. * Attributes will be removed from this set, if they belong to the filtered class, so * that by the time all classes are processed, allCfgs will be empty. * @param {Object} userVals The config object passed in by the user, from which adhoc attrs are to be filtered. * @return {Object} The set of adhoc attributes passed in, in the form * of an object with attribute name/configuration pairs. */ _filterAdHocAttrs : function(allAttrs, userVals) { var adHocs, nonAttrs = this._nonAttrs, attr; if (userVals) { adHocs = {}; for (attr in userVals) { if (!allAttrs[attr] && !nonAttrs[attr] && userVals.hasOwnProperty(attr)) { adHocs[attr] = { value:userVals[attr] }; } } } return adHocs; }, /** * A helper method used by _getClasses and _getAttrCfgs, which determines both * the array of classes and aggregate set of attribute configurations * across the class hierarchy for the instance. * * @method _initHierarchyData * @private */ _initHierarchyData : function() { var c = this.constructor, i, l, nonAttrsCfg, nonAttrs = (this._allowAdHocAttrs) ? {} : null, classes = [], attrs = []; while (c) { // Add to classes classes[classes.length] = c; // Add to attributes if (c.ATTRS) { attrs[attrs.length] = c.ATTRS; } if (this._allowAdHocAttrs) { nonAttrsCfg = c._NON_ATTRS_CFG; if (nonAttrsCfg) { for (i = 0, l = nonAttrsCfg.length; i < l; i++) { nonAttrs[nonAttrsCfg[i]] = true; } } } c = c.superclass ? c.superclass.constructor : null; } this._classes = classes; this._nonAttrs = nonAttrs; this._attrs = this._aggregateAttrs(attrs); }, /** * Utility method to define the attribute hash used to filter/whitelist property mixes for * this class. * * @method _attrCfgHash * @private */ _attrCfgHash: function() { return BaseCore._ATTR_CFG_HASH; }, /** * A helper method, used by _initHierarchyData to aggregate * attribute configuration across the instances class hierarchy. * * The method will protect the attribute configuration value to protect the statically defined * default value in ATTRS if required (if the value is an object literal, array or the * attribute configuration has cloneDefaultValue set to shallow or deep). * * @method _aggregateAttrs * @private * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy * (subclass first, Base last) * @return {Object} The aggregate set of ATTRS definitions for the instance */ _aggregateAttrs : function(allAttrs) { var attr, attrs, cfg, val, path, i, clone, cfgPropsHash = this._attrCfgHash(), aggAttrs = {}; if (allAttrs) { for (i = allAttrs.length-1; i >= 0; --i) { attrs = allAttrs[i]; for (attr in attrs) { if (attrs.hasOwnProperty(attr)) { // Protect config passed in //cfg = Y.mix({}, attrs[attr], true, cfgProps); //cfg = Y.Object(attrs[attr]); cfg = _wlmix({}, attrs[attr], cfgPropsHash); val = cfg.value; clone = cfg.cloneDefaultValue; if (val) { if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) { cfg.value = Y.clone(val); } else if (clone === SHALLOW) { cfg.value = Y.merge(val); } // else if (clone === false), don't clone the static default value. // It's intended to be used by reference. } path = null; if (attr.indexOf(DOT) !== -1) { path = attr.split(DOT); attr = path.shift(); } if (path && aggAttrs[attr] && aggAttrs[attr].value) { O.setValue(aggAttrs[attr].value, path, val); } else if (!path) { if (!aggAttrs[attr]) { aggAttrs[attr] = cfg; } else { _wlmix(aggAttrs[attr], cfg, cfgPropsHash); } } } } } } return aggAttrs; }, /** * Initializes the class hierarchy for the instance, which includes * initializing attributes for each class defined in the class's * static ATTRS property and * invoking the initializer method on the prototype of each class in the hierarchy. * * @method _initHierarchy * @param {Object} userVals Object with configuration property name/value pairs * @private */ _initHierarchy : function(userVals) { var lazy = this._lazyAddAttrs, constr, constrProto, ci, ei, el, extProto, exts, classes = this._getClasses(), attrCfgs = this._getAttrCfgs(), cl = classes.length - 1; for (ci = cl; ci >= 0; ci--) { constr = classes[ci]; constrProto = constr.prototype; exts = constr._yuibuild && constr._yuibuild.exts; if (exts) { for (ei = 0, el = exts.length; ei < el; ei++) { exts[ei].apply(this, arguments); } } this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy); if (this._allowAdHocAttrs && ci === cl) { this.addAttrs(this._filterAdHocAttrs(attrCfgs, userVals), userVals, lazy); } // Using INITIALIZER in hasOwnProperty check, for performance reasons (helps IE6 avoid GC thresholds when // referencing string literals). Not using it in apply, again, for performance "." is faster. if (constrProto.hasOwnProperty(INITIALIZER)) { constrProto.initializer.apply(this, arguments); } if (exts) { for (ei = 0; ei < el; ei++) { extProto = exts[ei].prototype; if (extProto.hasOwnProperty(INITIALIZER)) { extProto.initializer.apply(this, arguments); } } } } }, /** * Destroys the class hierarchy for this instance by invoking * the destructor method on the prototype of each class in the hierarchy. * * @method _destroyHierarchy * @private */ _destroyHierarchy : function() { var constr, constrProto, ci, cl, ei, el, exts, extProto, classes = this._getClasses(); for (ci = 0, cl = classes.length; ci < cl; ci++) { constr = classes[ci]; constrProto = constr.prototype; exts = constr._yuibuild && constr._yuibuild.exts; if (exts) { for (ei = 0, el = exts.length; ei < el; ei++) { extProto = exts[ei].prototype; if (extProto.hasOwnProperty(DESTRUCTOR)) { extProto.destructor.apply(this, arguments); } } } if (constrProto.hasOwnProperty(DESTRUCTOR)) { constrProto.destructor.apply(this, arguments); } } }, /** * Default toString implementation. Provides the constructor NAME * and the instance guid, if set. * * @method toString * @return {String} String representation for this object */ toString: function() { return this.name + "[" + Y.stamp(this, true) + "]"; } }; // Straightup augment, no wrapper functions Y.mix(BaseCore, AttributeCore, false, null, 1); // Fix constructor BaseCore.prototype.constructor = BaseCore; Y.BaseCore = BaseCore; }, '3.5.1' ,{requires:['attribute-core']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('event-custom-complex', function(Y) { /** * Adds event facades, preventable default behavior, and bubbling. * events. * @module event-custom * @submodule event-custom-complex */ var FACADE, FACADE_KEYS, EMPTY = {}, CEProto = Y.CustomEvent.prototype, ETProto = Y.EventTarget.prototype; /** * Wraps and protects a custom event for use when emitFacade is set to true. * Requires the event-custom-complex module * @class EventFacade * @param e {Event} the custom event * @param currentTarget {HTMLElement} the element the listener was attached to */ Y.EventFacade = function(e, currentTarget) { e = e || EMPTY; this._event = e; /** * The arguments passed to fire * @property details * @type Array */ this.details = e.details; /** * The event type, this can be overridden by the fire() payload * @property type * @type string */ this.type = e.type; /** * The real event type * @property _type * @type string * @private */ this._type = e.type; ////////////////////////////////////////////////////// /** * Node reference for the targeted eventtarget * @property target * @type Node */ this.target = e.target; /** * Node reference for the element that the listener was attached to. * @property currentTarget * @type Node */ this.currentTarget = currentTarget; /** * Node reference to the relatedTarget * @property relatedTarget * @type Node */ this.relatedTarget = e.relatedTarget; }; Y.extend(Y.EventFacade, Object, { /** * Stops the propagation to the next bubble target * @method stopPropagation */ stopPropagation: function() { this._event.stopPropagation(); this.stopped = 1; }, /** * Stops the propagation to the next bubble target and * prevents any additional listeners from being exectued * on the current target. * @method stopImmediatePropagation */ stopImmediatePropagation: function() { this._event.stopImmediatePropagation(); this.stopped = 2; }, /** * Prevents the event's default behavior * @method preventDefault */ preventDefault: function() { this._event.preventDefault(); this.prevented = 1; }, /** * Stops the event propagation and prevents the default * event behavior. * @method halt * @param immediate {boolean} if true additional listeners * on the current target will not be executed */ halt: function(immediate) { this._event.halt(immediate); this.prevented = 1; this.stopped = (immediate) ? 2 : 1; } }); CEProto.fireComplex = function(args) { var es, ef, q, queue, ce, ret, events, subs, postponed, self = this, host = self.host || self, next, oldbubble; if (self.stack) { // queue this event if the current item in the queue bubbles if (self.queuable && self.type != self.stack.next.type) { self.log('queue ' + self.type); self.stack.queue.push([self, args]); return true; } } es = self.stack || { // id of the first event in the stack id: self.id, next: self, silent: self.silent, stopped: 0, prevented: 0, bubbling: null, type: self.type, // defaultFnQueue: new Y.Queue(), afterQueue: new Y.Queue(), defaultTargetOnly: self.defaultTargetOnly, queue: [] }; subs = self.getSubs(); self.stopped = (self.type !== es.type) ? 0 : es.stopped; self.prevented = (self.type !== es.type) ? 0 : es.prevented; self.target = self.target || host; events = new Y.EventTarget({ fireOnce: true, context: host }); self.events = events; if (self.stoppedFn) { events.on('stopped', self.stoppedFn); } self.currentTarget = host; self.details = args.slice(); // original arguments in the details // self.log("Firing " + self + ", " + "args: " + args); self.log("Firing " + self.type); self._facade = null; // kill facade to eliminate stale properties ef = self._getFacade(args); if (Y.Lang.isObject(args[0])) { args[0] = ef; } else { args.unshift(ef); } // if (subCount) { if (subs[0]) { // self._procSubs(Y.merge(self.subscribers), args, ef); self._procSubs(subs[0], args, ef); } // bubble if this is hosted in an event target and propagation has not been stopped if (self.bubbles && host.bubble && !self.stopped) { oldbubble = es.bubbling; // self.bubbling = true; es.bubbling = self.type; // if (host !== ef.target || es.type != self.type) { if (es.type != self.type) { es.stopped = 0; es.prevented = 0; } ret = host.bubble(self, args, null, es); self.stopped = Math.max(self.stopped, es.stopped); self.prevented = Math.max(self.prevented, es.prevented); // self.bubbling = false; es.bubbling = oldbubble; } if (self.prevented) { if (self.preventedFn) { self.preventedFn.apply(host, args); } } else if (self.defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) { self.defaultFn.apply(host, args); } // broadcast listeners are fired as discreet events on the // YUI instance and potentially the YUI global. self._broadcast(args); // Queue the after if (subs[1] && !self.prevented && self.stopped < 2) { if (es.id === self.id || self.type != host._yuievt.bubbling) { self._procSubs(subs[1], args, ef); while ((next = es.afterQueue.last())) { next(); } } else { postponed = subs[1]; if (es.execDefaultCnt) { postponed = Y.merge(postponed); Y.each(postponed, function(s) { s.postponed = true; }); } es.afterQueue.add(function() { self._procSubs(postponed, args, ef); }); } } self.target = null; if (es.id === self.id) { queue = es.queue; while (queue.length) { q = queue.pop(); ce = q[0]; // set up stack to allow the next item to be processed es.next = ce; ce.fire.apply(ce, q[1]); } self.stack = null; } ret = !(self.stopped); if (self.type != host._yuievt.bubbling) { es.stopped = 0; es.prevented = 0; self.stopped = 0; self.prevented = 0; } return ret; }; CEProto._getFacade = function() { var ef = this._facade, o, o2, args = this.details; if (!ef) { ef = new Y.EventFacade(this, this.currentTarget); } // if the first argument is an object literal, apply the // properties to the event facade o = args && args[0]; if (Y.Lang.isObject(o, true)) { o2 = {}; // protect the event facade properties Y.mix(o2, ef, true, FACADE_KEYS); // mix the data Y.mix(ef, o, true); // restore ef Y.mix(ef, o2, true, FACADE_KEYS); // Allow the event type to be faked // http://yuilibrary.com/projects/yui3/ticket/2528376 ef.type = o.type || ef.type; } // update the details field with the arguments // ef.type = this.type; ef.details = this.details; // use the original target when the event bubbled to this target ef.target = this.originalTarget || this.target; ef.currentTarget = this.currentTarget; ef.stopped = 0; ef.prevented = 0; this._facade = ef; return this._facade; }; /** * Stop propagation to bubble targets * @for CustomEvent * @method stopPropagation */ CEProto.stopPropagation = function() { this.stopped = 1; if (this.stack) { this.stack.stopped = 1; } this.events.fire('stopped', this); }; /** * Stops propagation to bubble targets, and prevents any remaining * subscribers on the current target from executing. * @method stopImmediatePropagation */ CEProto.stopImmediatePropagation = function() { this.stopped = 2; if (this.stack) { this.stack.stopped = 2; } this.events.fire('stopped', this); }; /** * Prevents the execution of this event's defaultFn * @method preventDefault */ CEProto.preventDefault = function() { if (this.preventable) { this.prevented = 1; if (this.stack) { this.stack.prevented = 1; } } }; /** * Stops the event propagation and prevents the default * event behavior. * @method halt * @param immediate {boolean} if true additional listeners * on the current target will not be executed */ CEProto.halt = function(immediate) { if (immediate) { this.stopImmediatePropagation(); } else { this.stopPropagation(); } this.preventDefault(); }; /** * Registers another EventTarget as a bubble target. Bubble order * is determined by the order registered. Multiple targets can * be specified. * * Events can only bubble if emitFacade is true. * * Included in the event-custom-complex submodule. * * @method addTarget * @param o {EventTarget} the target to add * @for EventTarget */ ETProto.addTarget = function(o) { this._yuievt.targets[Y.stamp(o)] = o; this._yuievt.hasTargets = true; }; /** * Returns an array of bubble targets for this object. * @method getTargets * @return EventTarget[] */ ETProto.getTargets = function() { return Y.Object.values(this._yuievt.targets); }; /** * Removes a bubble target * @method removeTarget * @param o {EventTarget} the target to remove * @for EventTarget */ ETProto.removeTarget = function(o) { delete this._yuievt.targets[Y.stamp(o)]; }; /** * Propagate an event. Requires the event-custom-complex module. * @method bubble * @param evt {CustomEvent} the custom event to propagate * @return {boolean} the aggregated return value from Event.Custom.fire * @for EventTarget */ ETProto.bubble = function(evt, args, target, es) { var targs = this._yuievt.targets, ret = true, t, type = evt && evt.type, ce, i, bc, ce2, originalTarget = target || (evt && evt.target) || this, oldbubble; if (!evt || ((!evt.stopped) && targs)) { for (i in targs) { if (targs.hasOwnProperty(i)) { t = targs[i]; ce = t.getEvent(type, true); ce2 = t.getSibling(type, ce); if (ce2 && !ce) { ce = t.publish(type); } oldbubble = t._yuievt.bubbling; t._yuievt.bubbling = type; // if this event was not published on the bubble target, // continue propagating the event. if (!ce) { if (t._yuievt.hasTargets) { t.bubble(evt, args, originalTarget, es); } } else { ce.sibling = ce2; // set the original target to that the target payload on the // facade is correct. ce.target = originalTarget; ce.originalTarget = originalTarget; ce.currentTarget = t; bc = ce.broadcast; ce.broadcast = false; // default publish may not have emitFacade true -- that // shouldn't be what the implementer meant to do ce.emitFacade = true; ce.stack = es; ret = ret && ce.fire.apply(ce, args || evt.details || []); ce.broadcast = bc; ce.originalTarget = null; // stopPropagation() was called if (ce.stopped) { break; } } t._yuievt.bubbling = oldbubble; } } } return ret; }; FACADE = new Y.EventFacade(); FACADE_KEYS = Y.Object.keys(FACADE); }, '3.5.1' ,{requires:['event-custom-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-events', function(Y) { /** * The attribute module provides an augmentable Attribute implementation, which * adds configurable attributes and attribute change events to the class being * augmented. It also provides a State class, which is used internally by Attribute, * but can also be used independently to provide a name/property/value data structure to * store state. * * @module attribute */ /** * The attribute-events submodule provides augmentable attribute change event support * for AttributeCore based implementations. * * @module attribute * @submodule attribute-events */ var EventTarget = Y.EventTarget, CHANGE = "Change", BROADCAST = "broadcast", PUBLISHED = "published"; /** * Provides an augmentable implementation of attribute change events for * AttributeCore. * * @class AttributeEvents * @uses EventTarget */ function AttributeEvents() { // Perf tweak - avoid creating event literals if not required. this._ATTR_E_FACADE = {}; EventTarget.call(this, {emitFacade:true}); } AttributeEvents._ATTR_CFG = [BROADCAST]; AttributeEvents.prototype = { /** * Sets the value of an attribute. * * @method set * @chainable * * @param {String} name The name of the attribute. If the * current value of the attribute is an Object, dot notation can be used * to set the value of a property within the object (e.g. set("x.y.z", 5)). * * @param {Any} value The value to set the attribute to. * * @param {Object} opts (Optional) Optional event data to be mixed into * the event facade passed to subscribers of the attribute's change event. This * can be used as a flexible way to identify the source of a call to set, allowing * the developer to distinguish between set called internally by the host, vs. * set called externally by the application developer. * * @return {Object} A reference to the host object. */ set : function(name, val, opts) { return this._setAttr(name, val, opts); }, /** * Allows setting of readOnly/writeOnce attributes. See set for argument details. * * @method _set * @protected * @chainable * * @param {String} name The name of the attribute. * @param {Any} val The value to set the attribute to. * @param {Object} opts (Optional) Optional event data to be mixed into * the event facade passed to subscribers of the attribute's change event. * @return {Object} A reference to the host object. */ _set : function(name, val, opts) { return this._setAttr(name, val, opts, true); }, /** * Sets multiple attribute values. * * @method setAttrs * @param {Object} attrs An object with attributes name/value pairs. * @return {Object} A reference to the host object. * @chainable */ setAttrs : function(attrs, opts) { return this._setAttrs(attrs, opts); }, /** * Utility method to help setup the event payload and fire the attribute change event. * * @method _fireAttrChange * @private * @param {String} attrName The name of the attribute * @param {String} subAttrName The full path of the property being changed, * if this is a sub-attribute value being change. Otherwise null. * @param {Any} currVal The current value of the attribute * @param {Any} newVal The new value of the attribute * @param {Object} opts Any additional event data to mix into the attribute change event's event facade. */ _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) { var host = this, eventName = attrName + CHANGE, state = host._state, facade, broadcast, evtCfg; if (!state.get(attrName, PUBLISHED)) { evtCfg = { queuable:false, defaultTargetOnly: true, defaultFn:host._defAttrChangeFn, silent:true }; broadcast = state.get(attrName, BROADCAST); if (broadcast !== undefined) { evtCfg.broadcast = broadcast; } host.publish(eventName, evtCfg); state.add(attrName, PUBLISHED, true); } facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE; // Not using the single object signature for fire({type:..., newVal:...}), since // we don't want to override type. Changed to the fire(type, {newVal:...}) signature. // facade.type = eventName; facade.attrName = attrName; facade.subAttrName = subAttrName; facade.prevVal = currVal; facade.newVal = newVal; // host.fire(facade); host.fire(eventName, facade); }, /** * Default function for attribute change events. * * @private * @method _defAttrChangeFn * @param {EventFacade} e The event object for attribute change events. */ _defAttrChangeFn : function(e) { if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) { // Prevent "after" listeners from being invoked since nothing changed. e.stopImmediatePropagation(); } else { e.newVal = this.get(e.attrName); } } }; // Basic prototype augment - no lazy constructor invocation. Y.mix(AttributeEvents, EventTarget, false, null, 1); Y.AttributeEvents = AttributeEvents; }, '3.5.1' ,{requires:['event-custom']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-extras', function(Y) { /** * The attribute module provides an augmentable Attribute implementation, which * adds configurable attributes and attribute change events to the class being * augmented. It also provides a State class, which is used internally by Attribute, * but can also be used independently to provide a name/property/value data structure to * store state. * * @module attribute */ /** * The attribute-extras submodule provides less commonly used attribute methods, and can * be augmented/mixed into an implemention which used attribute-core. * * @module attribute * @submodule attribute-extras */ var BROADCAST = "broadcast", PUBLISHED = "published", INIT_VALUE = "initValue", MODIFIABLE = { readOnly:1, writeOnce:1, getter:1, broadcast:1 }; /** * A augmentable implementation for AttributeCore, providing less frequently used * methods for Attribute management such as modifyAttrs(), removeAttr and reset() * * @class AttributeExtras */ function AttributeExtras() {} AttributeExtras.prototype = { /** * Updates the configuration of an attribute which has already been added. *

* The properties which can be modified through this interface are limited * to the following subset of attributes, which can be safely modified * after a value has already been set on the attribute: readOnly, writeOnce, * broadcast and getter. *

* @method modifyAttr * @param {String} name The name of the attribute whose configuration is to be updated. * @param {Object} config An object with configuration property/value pairs, specifying the configuration properties to modify. */ modifyAttr: function(name, config) { var host = this, // help compression prop, state; if (host.attrAdded(name)) { if (host._isLazyAttr(name)) { host._addLazyAttr(name); } state = host._state; for (prop in config) { if (MODIFIABLE[prop] && config.hasOwnProperty(prop)) { state.add(name, prop, config[prop]); // If we reconfigured broadcast, need to republish if (prop === BROADCAST) { state.remove(name, PUBLISHED); } } } } }, /** * Removes an attribute from the host object * * @method removeAttr * @param {String} name The name of the attribute to be removed. */ removeAttr: function(name) { this._state.removeAll(name); }, /** * Resets the attribute (or all attributes) to its initial value, as long as * the attribute is not readOnly, or writeOnce. * * @method reset * @param {String} name Optional. The name of the attribute to reset. If omitted, all attributes are reset. * @return {Object} A reference to the host object. * @chainable */ reset : function(name) { var host = this; // help compression if (name) { if (host._isLazyAttr(name)) { host._addLazyAttr(name); } host.set(name, host._state.get(name, INIT_VALUE)); } else { Y.each(host._state.data, function(v, n) { host.reset(n); }); } return host; }, /** * Returns an object with the configuration properties (and value) * for the given attribute. If attrName is not provided, returns the * configuration properties for all attributes. * * @method _getAttrCfg * @protected * @param {String} name Optional. The attribute name. If not provided, the method will return the configuration for all attributes. * @return {Object} The configuration properties for the given attribute, or all attributes. */ _getAttrCfg : function(name) { var o, state = this._state; if (name) { o = state.getAll(name) || {}; } else { o = {}; Y.each(state.data, function(v, n) { o[n] = state.getAll(n); }); } return o; } }; Y.AttributeExtras = AttributeExtras; }, '3.5.1' ); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-base', function(Y) { /** * The attribute module provides an augmentable Attribute implementation, which * adds configurable attributes and attribute change events to the class being * augmented. It also provides a State class, which is used internally by Attribute, * but can also be used independently to provide a name/property/value data structure to * store state. * * @module attribute */ /** * The attribute-base submodule provides core attribute handling support, with everything * aside from complex attribute handling in the provider's constructor. * * @module attribute * @submodule attribute-base */ /** *

* Attribute provides configurable attribute support along with attribute change events. It is designed to be * augmented on to a host class, and provides the host with the ability to configure attributes to store and retrieve state, * along with attribute change events. *

*

For example, attributes added to the host can be configured:

* * *

See the addAttr method, for the complete set of configuration * options available for attributes.

* *

NOTE: Most implementations will be better off extending the Base class, * instead of augmenting Attribute directly. Base augments Attribute and will handle the initial configuration * of attributes for derived classes, accounting for values passed into the constructor.

* * @class Attribute * @param attrs {Object} The attributes to add during construction (passed through to addAttrs). These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. * @param values {Object} The initial attribute values to apply (passed through to addAttrs). These are not merged/cloned. The caller is responsible for isolating user provided values if required. * @param lazy {boolean} Whether or not to add attributes lazily (passed through to addAttrs). * @uses AttributeCore * @uses AttributeEvents * @uses EventTarget * @uses AttributeExtras */ var Attribute = function() { // Fix #2531929 // Complete hack, to make sure the first clone of a node value in IE doesn't doesn't hurt state - maintains 3.4.1 behavior. // Too late in the release cycle to do anything about the core problem. // The root issue is that cloning a Y.Node instance results in an object which barfs in IE, when you access it's properties (since 3.3.0). this._ATTR_E_FACADE = null; this._yuievt = null; Y.AttributeCore.apply(this, arguments); Y.AttributeEvents.apply(this, arguments); Y.AttributeExtras.apply(this, arguments); }; Y.mix(Attribute, Y.AttributeCore, false, null, 1); Y.mix(Attribute, Y.AttributeExtras, false, null, 1); // Needs to be "true", to overwrite methods from AttributeCore Y.mix(Attribute, Y.AttributeEvents, true, null, 1); /** *

The value to return from an attribute setter in order to prevent the set from going through.

* *

You can return this value from your setter if you wish to combine validator and setter * functionality into a single setter function, which either returns the massaged value to be stored or * AttributeCore.INVALID_VALUE to prevent invalid values from being stored.

* * @property INVALID_VALUE * @type Object * @static * @final */ Attribute.INVALID_VALUE = Y.AttributeCore.INVALID_VALUE; /** * The list of properties which can be configured for * each attribute (e.g. setter, getter, writeOnce etc.). * * This property is used internally as a whitelist for faster * Y.mix operations. * * @property _ATTR_CFG * @type Array * @static * @protected */ Attribute._ATTR_CFG = Y.AttributeCore._ATTR_CFG.concat(Y.AttributeEvents._ATTR_CFG); Y.Attribute = Attribute; }, '3.5.1' ,{requires:['attribute-core', 'attribute-events', 'attribute-extras']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-complex', function(Y) { /** * Adds support for attribute providers to handle complex attributes in the constructor * * @module attribute * @submodule attribute-complex * @for Attribute */ var O = Y.Object, DOT = "."; Y.Attribute.Complex = function() {}; Y.Attribute.Complex.prototype = { /** * Utility method to split out simple attribute name/value pairs ("x") * from complex attribute name/value pairs ("x.y.z"), so that complex * attributes can be keyed by the top level attribute name. * * @method _normAttrVals * @param {Object} valueHash An object with attribute name/value pairs * * @return {Object} An object literal with 2 properties - "simple" and "complex", * containing simple and complex attribute values respectively keyed * by the top level attribute name, or null, if valueHash is falsey. * * @private */ _normAttrVals : function(valueHash) { var vals = {}, subvals = {}, path, attr, v, k; if (valueHash) { for (k in valueHash) { if (valueHash.hasOwnProperty(k)) { if (k.indexOf(DOT) !== -1) { path = k.split(DOT); attr = path.shift(); v = subvals[attr] = subvals[attr] || []; v[v.length] = { path : path, value: valueHash[k] }; } else { vals[k] = valueHash[k]; } } } return { simple:vals, complex:subvals }; } else { return null; } }, /** * Returns the initial value of the given attribute from * either the default configuration provided, or the * over-ridden value if it exists in the set of initValues * provided and the attribute is not read-only. * * @param {String} attr The name of the attribute * @param {Object} cfg The attribute configuration object * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals * * @return {Any} The initial value of the attribute. * * @method _getAttrInitVal * @private */ _getAttrInitVal : function(attr, cfg, initValues) { var val = cfg.value, valFn = cfg.valueFn, simple, complex, i, l, path, subval, subvals; if (valFn) { if (!valFn.call) { valFn = this[valFn]; } if (valFn) { val = valFn.call(this, attr); } } if (!cfg.readOnly && initValues) { // Simple Attributes simple = initValues.simple; if (simple && simple.hasOwnProperty(attr)) { val = simple[attr]; } // Complex Attributes (complex values applied, after simple, incase both are set) complex = initValues.complex; if (complex && complex.hasOwnProperty(attr)) { subvals = complex[attr]; for (i = 0, l = subvals.length; i < l; ++i) { path = subvals[i].path; subval = subvals[i].value; O.setValue(val, path, subval); } } } return val; } }; Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1); // Consistency with the rest of the Attribute addons for now. Y.AttributeComplex = Y.Attribute.Complex; }, '3.5.1' ,{requires:['attribute-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('base-base', function(Y) { /** * The base module provides the Base class, which objects requiring attribute and custom event support can extend. * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides * plugin support and also provides the BaseCore.build method which provides a way to build custom classes using extensions. * * @module base */ /** * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host, * and without the extension support provided by BaseCore.build. * * @module base * @submodule base-base */ /** * The base module provides the Base class, which objects requiring attribute and custom event support can extend. * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides * plugin support and also provides the Base.build method which provides a way to build custom classes using extensions. * * @module base */ /** * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host, * and without the extension support provided by Base.build. * * @module base * @submodule base-base */ var L = Y.Lang, DESTROY = "destroy", INIT = "init", BUBBLETARGETS = "bubbleTargets", _BUBBLETARGETS = "_bubbleTargets", BaseCore = Y.BaseCore, AttributeCore = Y.AttributeCore, Attribute = Y.Attribute; /** *

* A base class which objects requiring attributes and custom event support can * extend. Base also handles the chaining of initializer and destructor methods across * the hierarchy as part of object construction and destruction. Additionally, attributes configured * through the static ATTRS property for each class * in the hierarchy will be initialized by Base. *

* *

* The static NAME property of each class extending * from Base will be used as the identifier for the class, and is used by Base to prefix * all events fired by instances of that class. *

* * @class Base * @constructor * @uses BaseCore * @uses Attribute * @uses AttributeCore * @uses AttributeEvents * @uses AttributeExtras * @uses EventTarget * * @param {Object} config Object with configuration property name/value pairs. The object can be * used to provide default values for the objects published attributes. * *

* The config object can also contain the following non-attribute properties, providing a convenient * way to configure events listeners and plugins for the instance, as part of the constructor call: *

* *
*
on
*
An event name to listener function map, to register event listeners for the "on" moment of the event. A constructor convenience property for the on method.
*
after
*
An event name to listener function map, to register event listeners for the "after" moment of the event. A constructor convenience property for the after method.
*
bubbleTargets
*
An object, or array of objects, to register as bubble targets for bubbled events fired by this instance. A constructor convenience property for the addTarget method.
*
plugins
*
A plugin, or array of plugins to be plugged into the instance (see PluginHost's plug method for signature details). A constructor convenience property for the plug method.
*
*/ function Base() { BaseCore.apply(this, arguments); } /** * The list of properties which can be configured for * each attribute (e.g. setter, getter, writeOnce, readOnly etc.) * * @property _ATTR_CFG * @type Array * @static * @private */ Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue"); Base._ATTR_CFG_HASH = Y.Array.hash(Base._ATTR_CFG); /** * The array of non-attribute configuration properties supported by this class. * * `Base` supports "on", "after", "plugins" and "bubbleTargets" properties, * which are not set up as attributes. * * This property is primarily required so that when * `_allowAdHocAttrs` is enabled by * a class, non-attribute configurations don't get added as ad-hoc attributes. * * @property _NON_ATTRS_CFG * @type Array * @static * @private */ Base._NON_ATTRS_CFG = BaseCore._NON_ATTRS_CFG.concat(["on", "after", "bubbleTargets"]); /** *

* The string to be used to identify instances of * this class, for example in prefixing events. *

*

* Classes extending Base, should define their own * static NAME property, which should be camelCase by * convention (e.g. MyClass.NAME = "myClass";). *

* @property NAME * @type String * @static */ Base.NAME = "base"; /** * The default set of attributes which will be available for instances of this class, and * their configuration. In addition to the configuration properties listed by * Attribute's addAttr method, the attribute * can also be configured with a "cloneDefaultValue" property, which defines how the statically * defined value field should be protected ("shallow", "deep" and false are supported values). * * By default if the value is an object literal or an array it will be "shallow" cloned, to * protect the default value. * * @property ATTRS * @type Object * @static */ Base.ATTRS = AttributeCore.prototype._protectAttrs(BaseCore.ATTRS); Base.prototype = { /** * Internal construction logic for Base. * * @method _initBase * @param {Object} config The constructor configuration object * @private */ _initBase: function(cfg) { this._eventPrefix = this.constructor.EVENT_PREFIX || this.constructor.NAME; Y.BaseCore.prototype._initBase.call(this, cfg); }, /** * Initializes Attribute * * @method _initAttribute * @private */ _initAttribute: function(cfg) { Attribute.call(this); this._yuievt.config.prefix = this._eventPrefix; }, /** * Utility method to define the attribute hash used to filter/whitelist property mixes for * this class. * * @method _attrCfgHash * @private */ _attrCfgHash: function() { return Base._ATTR_CFG_HASH; }, /** * Init lifecycle method, invoked during construction. * Fires the init event prior to setting up attributes and * invoking initializers for the class hierarchy. * * @method init * @chainable * @param {Object} config Object with configuration property name/value pairs * @return {Base} A reference to this object */ init: function(config) { /** *

* Lifecycle event for the init phase, fired prior to initialization. * Invoking the preventDefault() method on the event object provided * to subscribers will prevent initialization from occuring. *

*

* Subscribers to the "after" momemt of this event, will be notified * after initialization of the object is complete (and therefore * cannot prevent initialization). *

* * @event init * @preventable _defInitFn * @param {EventFacade} e Event object, with a cfg property which * refers to the configuration object passed to the constructor. */ this.publish(INIT, { queuable:false, fireOnce:true, defaultTargetOnly:true, defaultFn:this._defInitFn }); this._preInitEventCfg(config); this.fire(INIT, {cfg: config}); return this; }, /** * Handles the special on, after and target properties which allow the user to * easily configure on and after listeners as well as bubble targets during * construction, prior to init. * * @private * @method _preInitEventCfg * @param {Object} config The user configuration object */ _preInitEventCfg : function(config) { if (config) { if (config.on) { this.on(config.on); } if (config.after) { this.after(config.after); } } var i, l, target, userTargets = (config && BUBBLETARGETS in config); if (userTargets || _BUBBLETARGETS in this) { target = userTargets ? (config && config.bubbleTargets) : this._bubbleTargets; if (L.isArray(target)) { for (i = 0, l = target.length; i < l; i++) { this.addTarget(target[i]); } } else if (target) { this.addTarget(target); } } }, /** *

* Destroy lifecycle method. Fires the destroy * event, prior to invoking destructors for the * class hierarchy. *

*

* Subscribers to the destroy * event can invoke preventDefault on the event object, to prevent destruction * from proceeding. *

* @method destroy * @return {Base} A reference to this object * @chainable */ destroy: function() { /** *

* Lifecycle event for the destroy phase, * fired prior to destruction. Invoking the preventDefault * method on the event object provided to subscribers will * prevent destruction from proceeding. *

*

* Subscribers to the "after" moment of this event, will be notified * after destruction is complete (and as a result cannot prevent * destruction). *

* @event destroy * @preventable _defDestroyFn * @param {EventFacade} e Event object */ this.publish(DESTROY, { queuable:false, fireOnce:true, defaultTargetOnly:true, defaultFn: this._defDestroyFn }); this.fire(DESTROY); this.detachAll(); return this; }, /** * Default init event handler * * @method _defInitFn * @param {EventFacade} e Event object, with a cfg property which * refers to the configuration object passed to the constructor. * @protected */ _defInitFn : function(e) { this._baseInit(e.cfg); }, /** * Default destroy event handler * * @method _defDestroyFn * @param {EventFacade} e Event object * @protected */ _defDestroyFn : function(e) { this._baseDestroy(e.cfg); } }; Y.mix(Base, Attribute, false, null, 1); Y.mix(Base, BaseCore, false, null, 1); // Fix constructor Base.prototype.constructor = Base; Y.Base = Base; }, '3.5.1' ,{requires:['base-core', 'attribute-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('base-build', function(Y) { /** * The base-build submodule provides Base.build functionality, which * can be used to create custom classes, by aggregating extensions onto * a main class. * * @module base * @submodule base-build * @for Base */ var Base = Y.Base, L = Y.Lang, INITIALIZER = "initializer", DESTRUCTOR = "destructor", build, arrayAggregator = function (prop, r, s) { if (s[prop]) { r[prop] = (r[prop] || []).concat(s[prop]); } }; Base._build = function(name, main, extensions, px, sx, cfg) { var build = Base._build, builtClass = build._ctor(main, cfg), buildCfg = build._cfg(main, cfg, extensions), _mixCust = build._mixCust, dynamic = builtClass._yuibuild.dynamic, i, l, extClass, extProto, initializer, destructor; // Augment/Aggregate for (i = 0, l = extensions.length; i < l; i++) { extClass = extensions[i]; extProto = extClass.prototype; initializer = extProto[INITIALIZER]; destructor = extProto[DESTRUCTOR]; delete extProto[INITIALIZER]; delete extProto[DESTRUCTOR]; // Prototype, old non-displacing augment Y.mix(builtClass, extClass, true, null, 1); // Custom Statics _mixCust(builtClass, extClass, buildCfg); if (initializer) { extProto[INITIALIZER] = initializer; } if (destructor) { extProto[DESTRUCTOR] = destructor; } builtClass._yuibuild.exts.push(extClass); } if (px) { Y.mix(builtClass.prototype, px, true); } if (sx) { Y.mix(builtClass, build._clean(sx, buildCfg), true); _mixCust(builtClass, sx, buildCfg); } builtClass.prototype.hasImpl = build._impl; if (dynamic) { builtClass.NAME = name; builtClass.prototype.constructor = builtClass; } return builtClass; }; build = Base._build; Y.mix(build, { _mixCust: function(r, s, cfg) { var aggregates, custom, statics, aggr, l, i; if (cfg) { aggregates = cfg.aggregates; custom = cfg.custom; statics = cfg.statics; } if (statics) { Y.mix(r, s, true, statics); } if (aggregates) { for (i = 0, l = aggregates.length; i < l; i++) { aggr = aggregates[i]; if (!r.hasOwnProperty(aggr) && s.hasOwnProperty(aggr)) { r[aggr] = L.isArray(s[aggr]) ? [] : {}; } Y.aggregate(r, s, true, [aggr]); } } if (custom) { for (i in custom) { if (custom.hasOwnProperty(i)) { custom[i](i, r, s); } } } }, _tmpl: function(main) { function BuiltClass() { BuiltClass.superclass.constructor.apply(this, arguments); } Y.extend(BuiltClass, main); return BuiltClass; }, _impl : function(extClass) { var classes = this._getClasses(), i, l, cls, exts, ll, j; for (i = 0, l = classes.length; i < l; i++) { cls = classes[i]; if (cls._yuibuild) { exts = cls._yuibuild.exts; ll = exts.length; for (j = 0; j < ll; j++) { if (exts[j] === extClass) { return true; } } } } return false; }, _ctor : function(main, cfg) { var dynamic = (cfg && false === cfg.dynamic) ? false : true, builtClass = (dynamic) ? build._tmpl(main) : main, buildCfg = builtClass._yuibuild; if (!buildCfg) { buildCfg = builtClass._yuibuild = {}; } buildCfg.id = buildCfg.id || null; buildCfg.exts = buildCfg.exts || []; buildCfg.dynamic = dynamic; return builtClass; }, _cfg : function(main, cfg, exts) { var aggr = [], cust = {}, statics = [], buildCfg, cfgAggr = (cfg && cfg.aggregates), cfgCustBuild = (cfg && cfg.custom), cfgStatics = (cfg && cfg.statics), c = main, i, l; // Prototype Chain while (c && c.prototype) { buildCfg = c._buildCfg; if (buildCfg) { if (buildCfg.aggregates) { aggr = aggr.concat(buildCfg.aggregates); } if (buildCfg.custom) { Y.mix(cust, buildCfg.custom, true); } if (buildCfg.statics) { statics = statics.concat(buildCfg.statics); } } c = c.superclass ? c.superclass.constructor : null; } // Exts if (exts) { for (i = 0, l = exts.length; i < l; i++) { c = exts[i]; buildCfg = c._buildCfg; if (buildCfg) { if (buildCfg.aggregates) { aggr = aggr.concat(buildCfg.aggregates); } if (buildCfg.custom) { Y.mix(cust, buildCfg.custom, true); } if (buildCfg.statics) { statics = statics.concat(buildCfg.statics); } } } } if (cfgAggr) { aggr = aggr.concat(cfgAggr); } if (cfgCustBuild) { Y.mix(cust, cfg.cfgBuild, true); } if (cfgStatics) { statics = statics.concat(cfgStatics); } return { aggregates: aggr, custom: cust, statics: statics }; }, _clean : function(sx, cfg) { var prop, i, l, sxclone = Y.merge(sx), aggregates = cfg.aggregates, custom = cfg.custom; for (prop in custom) { if (sxclone.hasOwnProperty(prop)) { delete sxclone[prop]; } } for (i = 0, l = aggregates.length; i < l; i++) { prop = aggregates[i]; if (sxclone.hasOwnProperty(prop)) { delete sxclone[prop]; } } return sxclone; } }); /** *

* Builds a custom constructor function (class) from the * main function, and array of extension functions (classes) * provided. The NAME field for the constructor function is * defined by the first argument passed in. *

*

* The cfg object supports the following properties *

*
*
dynamic <boolean>
*
*

If true (default), a completely new class * is created which extends the main class, and acts as the * host on which the extension classes are augmented.

*

If false, the extensions classes are augmented directly to * the main class, modifying the main class' prototype.

*
*
aggregates <String[]>
*
An array of static property names, which will get aggregated * on to the built class, in addition to the default properties build * will always aggregate as defined by the main class' static _buildCfg * property. *
*
* * @method build * @deprecated Use the more convenient Base.create and Base.mix methods instead * @static * @param {Function} name The name of the new class. Used to defined the NAME property for the new class. * @param {Function} main The main class on which to base the built class * @param {Function[]} extensions The set of extension classes which will be * augmented/aggregated to the built class. * @param {Object} cfg Optional. Build configuration for the class (see description). * @return {Function} A custom class, created from the provided main and extension classes */ Base.build = function(name, main, extensions, cfg) { return build(name, main, extensions, null, null, cfg); }; /** * Creates a new class (constructor function) which extends the base class passed in as the second argument, * and mixes in the array of extensions provided. * * Prototype properties or methods can be added to the new class, using the px argument (similar to Y.extend). * * Static properties or methods can be added to the new class, using the sx argument (similar to Y.extend). * * **NOTE FOR COMPONENT DEVELOPERS**: Both the `base` class, and `extensions` can define static a `_buildCfg` * property, which acts as class creation meta-data, and drives how special static properties from the base * class, or extensions should be copied, aggregated or (custom) mixed into the newly created class. * * The `_buildCfg` property is a hash with 3 supported properties: `statics`, `aggregates` and `custom`, e.g: * * // If the Base/Main class is the thing introducing the property: * * MyBaseClass._buildCfg = { * * // Static properties/methods to copy (Alias) to the built class. * statics: ["CopyThisMethod", "CopyThisProperty"], * * // Static props to aggregate onto the built class. * aggregates: ["AggregateThisProperty"], * * // Static properties which need custom handling (e.g. deep merge etc.) * custom: { * "CustomProperty" : function(property, Receiver, Supplier) { * ... * var triggers = Receiver.CustomProperty.triggers; Receiver.CustomProperty.triggers = triggers.concat(Supplier.CustomProperty.triggers); * ... * } * } * }; * * MyBaseClass.CopyThisMethod = function() {...}; * MyBaseClass.CopyThisProperty = "foo"; * MyBaseClass.AggregateThisProperty = {...}; * MyBaseClass.CustomProperty = { * triggers: [...] * } * * // Or, if the Extension is the thing introducing the property: * * MyExtension._buildCfg = { * statics : ... * aggregates : ... * custom : ... * } * * This way, when users pass your base or extension class to `Y.Base.create` or `Y.Base.mix`, they don't need to * know which properties need special handling. `Y.Base` has a buildCfg which defines `ATTRS` for custom mix handling * (to protect the static config objects), and `Y.Widget` has a buildCfg which specifies `HTML_PARSER` for * straight up aggregation. * * @method create * @static * @param {Function} name The name of the newly created class. Used to defined the NAME property for the new class. * @param {Function} main The base class which the new class should extend. This class needs to be Base or a class derived from base (e.g. Widget). * @param {Function[]} extensions The list of extensions which will be mixed into the built class. * @param {Object} px The set of prototype properties/methods to add to the built class. * @param {Object} sx The set of static properties/methods to add to the built class. * @return {Function} The newly created class. */ Base.create = function(name, base, extensions, px, sx) { return build(name, base, extensions, px, sx); }; /** *

Mixes in a list of extensions to an existing class.

* @method mix * @static * @param {Function} main The existing class into which the extensions should be mixed. The class needs to be Base or a class derived from Base (e.g. Widget) * @param {Function[]} extensions The set of extension classes which will mixed into the existing main class. * @return {Function} The modified main class, with extensions mixed in. */ Base.mix = function(main, extensions) { return build(null, main, extensions, null, null, {dynamic:false}); }; /** * The build configuration for the Base class. * * Defines the static fields which need to be aggregated * when the Base class is used as the main class passed to * the Base.build method. * * @property _buildCfg * @type Object * @static * @final * @private */ Base._buildCfg = { custom : { ATTRS : function(prop, r, s) { r.ATTRS = r.ATTRS || {}; if (s.ATTRS) { var sAttrs = s.ATTRS, rAttrs = r.ATTRS, a; for (a in sAttrs) { if (sAttrs.hasOwnProperty(a)) { rAttrs[a] = rAttrs[a] || {}; Y.mix(rAttrs[a], sAttrs[a], true); } } } }, _NON_ATTRS_CFG : arrayAggregator }, aggregates : ["_PLUG", "_UNPLUG"] }; }, '3.5.1' ,{requires:['base-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('base-pluginhost', function(Y) { /** * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods. * * @module base * @submodule base-pluginhost * @for Base */ var Base = Y.Base, PluginHost = Y.Plugin.Host; Y.mix(Base, PluginHost, false, null, 1); /** * Alias for Plugin.Host.plug. See aliased * method for argument and return value details. * * @method plug * @static */ Base.plug = PluginHost.plug; /** * Alias for Plugin.Host.unplug. See the * aliased method for argument and return value details. * * @method unplug * @static */ Base.unplug = PluginHost.unplug; }, '3.5.1' ,{requires:['base-base', 'pluginhost']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('querystring-stringify-simple', function(Y) { /*global Y */ /** *

Provides Y.QueryString.stringify method for converting objects to Query Strings. * This is a subset implementation of the full querystring-stringify.

*

This module provides the bare minimum functionality (encoding a hash of simple values), * without the additional support for nested data structures. Every key-value pair is * encoded by encodeURIComponent.

*

This module provides a minimalistic way for io to handle single-level objects * as transaction data.

* * @module querystring * @submodule querystring-stringify-simple * @for QueryString * @static */ var QueryString = Y.namespace("QueryString"), EUC = encodeURIComponent; /** *

Converts a simple object to a Query String representation.

*

Nested objects, Arrays, and so on, are not supported.

* * @method stringify * @for QueryString * @public * @submodule querystring-stringify-simple * @param obj {Object} A single-level object to convert to a querystring. * @param cfg {Object} (optional) Configuration object. In the simple * module, only the arrayKey setting is * supported. When set to true, the key of an * array will have the '[]' notation appended * to the key;. * @static */ QueryString.stringify = function (obj, c) { var qs = [], // Default behavior is false; standard key notation. s = c && c.arrayKey ? true : false, key, i, l; for (key in obj) { if (obj.hasOwnProperty(key)) { if (Y.Lang.isArray(obj[key])) { for (i = 0, l = obj[key].length; i < l; i++) { qs.push(EUC(s ? key + '[]' : key) + '=' + EUC(obj[key][i])); } } else { qs.push(EUC(key) + '=' + EUC(obj[key])); } } } return qs.join('&'); }; }, '3.5.1' ,{requires:['yui-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('io-base', function(Y) { /** Base IO functionality. Provides basic XHR transport support. @module io-base @main io-base @for IO **/ var // List of events that comprise the IO event lifecycle. EVENTS = ['start', 'complete', 'end', 'success', 'failure', 'progress'], // Whitelist of used XHR response object properties. XHR_PROPS = ['status', 'statusText', 'responseText', 'responseXML'], win = Y.config.win, uid = 0; /** The IO class is a utility that brokers HTTP requests through a simplified interface. Specifically, it allows JavaScript to make HTTP requests to a resource without a page reload. The underlying transport for making same-domain requests is the XMLHttpRequest object. IO can also use Flash, if specified as a transport, for cross-domain requests. @class IO @constructor @param {Object} config Object of EventTarget's publish method configurations used to configure IO's events. **/ function IO (config) { var io = this; io._uid = 'io:' + uid++; io._init(config); Y.io._map[io._uid] = io; } IO.prototype = { //-------------------------------------- // Properties //-------------------------------------- /** * A counter that increments for each transaction. * * @property _id * @private * @type {Number} */ _id: 0, /** * Object of IO HTTP headers sent with each transaction. * * @property _headers * @private * @type {Object} */ _headers: { 'X-Requested-With' : 'XMLHttpRequest' }, /** * Object that stores timeout values for any transaction with a defined * "timeout" configuration property. * * @property _timeout * @private * @type {Object} */ _timeout: {}, //-------------------------------------- // Methods //-------------------------------------- _init: function(config) { var io = this, i, len; io.cfg = config || {}; Y.augment(io, Y.EventTarget); for (i = 0, len = EVENTS.length; i < len; ++i) { // Publish IO global events with configurations, if any. // IO global events are set to broadcast by default. // These events use the "io:" namespace. io.publish('io:' + EVENTS[i], Y.merge({ broadcast: 1 }, config)); // Publish IO transaction events with configurations, if // any. These events use the "io-trn:" namespace. io.publish('io-trn:' + EVENTS[i], config); } }, /** * Method that creates a unique transaction object for each request. * * @method _create * @private * @param {Object} cfg Configuration object subset to determine if * the transaction is an XDR or file upload, * requiring an alternate transport. * @param {Number} id Transaction id * @return {Object} The transaction object */ _create: function(config, id) { var io = this, transaction = { id : Y.Lang.isNumber(id) ? id : io._id++, uid: io._uid }, alt = config.xdr ? config.xdr.use : null, form = config.form && config.form.upload ? 'iframe' : null, use; if (alt === 'native') { // Non-IE can use XHR level 2 and not rely on an // external transport. alt = Y.UA.ie ? 'xdr' : null; } use = alt || form; transaction = use ? Y.merge(Y.IO.customTransport(use), transaction) : Y.merge(Y.IO.defaultTransport(), transaction); if (transaction.notify) { config.notify = function (e, t, c) { io.notify(e, t, c); }; } if (!use) { if (win && win.FormData && config.data instanceof FormData) { transaction.c.upload.onprogress = function (e) { io.progress(transaction, e, config); }; transaction.c.onload = function (e) { io.load(transaction, e, config); }; transaction.c.onerror = function (e) { io.error(transaction, e, config); }; transaction.upload = true; } } return transaction; }, _destroy: function(transaction) { if (win && !transaction.notify && !transaction.xdr) { if (XHR && !transaction.upload) { transaction.c.onreadystatechange = null; } else if (transaction.upload) { transaction.c.upload.onprogress = null; transaction.c.onload = null; transaction.c.onerror = null; } else if (Y.UA.ie && !transaction.e) { // IE, when using XMLHttpRequest as an ActiveX Object, will throw // a "Type Mismatch" error if the event handler is set to "null". transaction.c.abort(); } } transaction = transaction.c = null; }, /** * Method for creating and firing events. * * @method _evt * @private * @param {String} eventName Event to be published. * @param {Object} transaction Transaction object. * @param {Object} config Configuration data subset for event subscription. */ _evt: function(eventName, transaction, config) { var io = this, params, args = config['arguments'], emitFacade = io.cfg.emitFacade, globalEvent = "io:" + eventName, trnEvent = "io-trn:" + eventName; // Workaround for #2532107 this.detach(trnEvent); if (transaction.e) { transaction.c = { status: 0, statusText: transaction.e }; } // Fire event with parameters or an Event Facade. params = [ emitFacade ? { id: transaction.id, data: transaction.c, cfg: config, 'arguments': args } : transaction.id ]; if (!emitFacade) { if (eventName === EVENTS[0] || eventName === EVENTS[2]) { if (args) { params.push(args); } } else { if (transaction.evt) { params.push(transaction.evt); } else { params.push(transaction.c); } if (args) { params.push(args); } } } params.unshift(globalEvent); // Fire global events. io.fire.apply(io, params); // Fire transaction events, if receivers are defined. if (config.on) { params[0] = trnEvent; io.once(trnEvent, config.on[eventName], config.context || Y); io.fire.apply(io, params); } }, /** * Fires event "io:start" and creates, fires a transaction-specific * start event, if `config.on.start` is defined. * * @method start * @param {Object} transaction Transaction object. * @param {Object} config Configuration object for the transaction. */ start: function(transaction, config) { /** * Signals the start of an IO request. * @event io:start */ this._evt(EVENTS[0], transaction, config); }, /** * Fires event "io:complete" and creates, fires a * transaction-specific "complete" event, if config.on.complete is * defined. * * @method complete * @param {Object} transaction Transaction object. * @param {Object} config Configuration object for the transaction. */ complete: function(transaction, config) { /** * Signals the completion of the request-response phase of a * transaction. Response status and data are accessible, if * available, in this event. * @event io:complete */ this._evt(EVENTS[1], transaction, config); }, /** * Fires event "io:end" and creates, fires a transaction-specific "end" * event, if config.on.end is defined. * * @method end * @param {Object} transaction Transaction object. * @param {Object} config Configuration object for the transaction. */ end: function(transaction, config) { /** * Signals the end of the transaction lifecycle. * @event io:end */ this._evt(EVENTS[2], transaction, config); this._destroy(transaction); }, /** * Fires event "io:success" and creates, fires a transaction-specific * "success" event, if config.on.success is defined. * * @method success * @param {Object} transaction Transaction object. * @param {Object} config Configuration object for the transaction. */ success: function(transaction, config) { /** * Signals an HTTP response with status in the 2xx range. * Fires after io:complete. * @event io:success */ this._evt(EVENTS[3], transaction, config); this.end(transaction, config); }, /** * Fires event "io:failure" and creates, fires a transaction-specific * "failure" event, if config.on.failure is defined. * * @method failure * @param {Object} transaction Transaction object. * @param {Object} config Configuration object for the transaction. */ failure: function(transaction, config) { /** * Signals an HTTP response with status outside of the 2xx range. * Fires after io:complete. * @event io:failure */ this._evt(EVENTS[4], transaction, config); this.end(transaction, config); }, /** * Fires event "io:progress" and creates, fires a transaction-specific * "progress" event -- for XMLHttpRequest file upload -- if * config.on.progress is defined. * * @method progress * @param {Object} transaction Transaction object. * @param {Object} progress event. * @param {Object} config Configuration object for the transaction. */ progress: function(transaction, e, config) { /** * Signals the interactive state during a file upload transaction. * This event fires after io:start and before io:complete. * @event io:progress */ transaction.evt = e; this._evt(EVENTS[5], transaction, config); }, /** * Fires event "io:complete" and creates, fires a transaction-specific * "complete" event -- for XMLHttpRequest file upload -- if * config.on.complete is defined. * * @method load * @param {Object} transaction Transaction object. * @param {Object} load event. * @param {Object} config Configuration object for the transaction. */ load: function (transaction, e, config) { transaction.evt = e.target; this._evt(EVENTS[1], transaction, config); }, /** * Fires event "io:failure" and creates, fires a transaction-specific * "failure" event -- for XMLHttpRequest file upload -- if * config.on.failure is defined. * * @method error * @param {Object} transaction Transaction object. * @param {Object} error event. * @param {Object} config Configuration object for the transaction. */ error: function (transaction, e, config) { transaction.evt = e; this._evt(EVENTS[4], transaction, config); }, /** * Retry an XDR transaction, using the Flash tranport, if the native * transport fails. * * @method _retry * @private * @param {Object} transaction Transaction object. * @param {String} uri Qualified path to transaction resource. * @param {Object} config Configuration object for the transaction. */ _retry: function(transaction, uri, config) { this._destroy(transaction); config.xdr.use = 'flash'; return this.send(uri, config, transaction.id); }, /** * Method that concatenates string data for HTTP GET transactions. * * @method _concat * @private * @param {String} uri URI or root data. * @param {String} data Data to be concatenated onto URI. * @return {String} */ _concat: function(uri, data) { uri += (uri.indexOf('?') === -1 ? '?' : '&') + data; return uri; }, /** * Stores default client headers for all transactions. If a label is * passed with no value argument, the header will be deleted. * * @method setHeader * @param {String} name HTTP header * @param {String} value HTTP header value */ setHeader: function(name, value) { if (value) { this._headers[name] = value; } else { delete this._headers[name]; } }, /** * Method that sets all HTTP headers to be sent in a transaction. * * @method _setHeaders * @private * @param {Object} transaction - XHR instance for the specific transaction. * @param {Object} headers - HTTP headers for the specific transaction, as * defined in the configuration object passed to YUI.io(). */ _setHeaders: function(transaction, headers) { headers = Y.merge(this._headers, headers); Y.Object.each(headers, function(value, name) { if (value !== 'disable') { transaction.setRequestHeader(name, headers[name]); } }); }, /** * Starts timeout count if the configuration object has a defined * timeout property. * * @method _startTimeout * @private * @param {Object} transaction Transaction object generated by _create(). * @param {Object} timeout Timeout in milliseconds. */ _startTimeout: function(transaction, timeout) { var io = this; io._timeout[transaction.id] = setTimeout(function() { io._abort(transaction, 'timeout'); }, timeout); }, /** * Clears the timeout interval started by _startTimeout(). * * @method _clearTimeout * @private * @param {Number} id - Transaction id. */ _clearTimeout: function(id) { clearTimeout(this._timeout[id]); delete this._timeout[id]; }, /** * Method that determines if a transaction response qualifies as success * or failure, based on the response HTTP status code, and fires the * appropriate success or failure events. * * @method _result * @private * @static * @param {Object} transaction Transaction object generated by _create(). * @param {Object} config Configuration object passed to io(). */ _result: function(transaction, config) { var status; // Firefox will throw an exception if attempting to access // an XHR object's status property, after a request is aborted. try { status = transaction.c.status; } catch(e) { status = 0; } // IE reports HTTP 204 as HTTP 1223. if (status >= 200 && status < 300 || status === 304 || status === 1223) { this.success(transaction, config); } else { this.failure(transaction, config); } }, /** * Event handler bound to onreadystatechange. * * @method _rS * @private * @param {Object} transaction Transaction object generated by _create(). * @param {Object} config Configuration object passed to YUI.io(). */ _rS: function(transaction, config) { var io = this; if (transaction.c.readyState === 4) { if (config.timeout) { io._clearTimeout(transaction.id); } // Yield in the event of request timeout or abort. setTimeout(function() { io.complete(transaction, config); io._result(transaction, config); }, 0); } }, /** * Terminates a transaction due to an explicit abort or timeout. * * @method _abort * @private * @param {Object} transaction Transaction object generated by _create(). * @param {String} type Identifies timed out or aborted transaction. */ _abort: function(transaction, type) { if (transaction && transaction.c) { transaction.e = type; transaction.c.abort(); } }, /** * Requests a transaction. `send()` is implemented as `Y.io()`. Each * transaction may include a configuration object. Its properties are: * *
*
method
*
HTTP method verb (e.g., GET or POST). If this property is not * not defined, the default value will be GET.
* *
data
*
This is the name-value string that will be sent as the * transaction data. If the request is HTTP GET, the data become * part of querystring. If HTTP POST, the data are sent in the * message body.
* *
xdr
*
Defines the transport to be used for cross-domain requests. * By setting this property, the transaction will use the specified * transport instead of XMLHttpRequest. The properties of the * transport object are: *
*
use
*
The transport to be used: 'flash' or 'native'
*
dataType
*
Set the value to 'XML' if that is the expected response * content type.
*
* *
form
*
Form serialization configuration object. Its properties are: *
*
id
*
Node object or id of HTML form
*
useDisabled
*
`true` to also serialize disabled form field values * (defaults to `false`)
*
* *
on
*
Assigns transaction event subscriptions. Available events are: *
*
start
*
Fires when a request is sent to a resource.
*
complete
*
Fires when the transaction is complete.
*
success
*
Fires when the HTTP response status is within the 2xx * range.
*
failure
*
Fires when the HTTP response status is outside the 2xx * range, if an exception occurs, if the transation is aborted, * or if the transaction exceeds a configured `timeout`.
*
end
*
Fires at the conclusion of the transaction * lifecycle, after `success` or `failure`.
*
* *

Callback functions for `start` and `end` receive the id of the * transaction as a first argument. For `complete`, `success`, and * `failure`, callbacks receive the id and the response object * (usually the XMLHttpRequest instance). If the `arguments` * property was included in the configuration object passed to * `Y.io()`, the configured data will be passed to all callbacks as * the last argument.

*
* *
sync
*
Pass `true` to make a same-domain transaction synchronous. * CAVEAT: This will negatively impact the user * experience. Have a very good reason if you intend to use * this.
* *
context
*
The "`this'" object for all configured event handlers. If a * specific context is needed for individual callbacks, bind the * callback to a context using `Y.bind()`.
* *
headers
*
Object map of transaction headers to send to the server. The * object keys are the header names and the values are the header * values.
* *
timeout
*
Millisecond threshold for the transaction before being * automatically aborted.
* *
arguments
*
User-defined data passed to all registered event handlers. * This value is available as the second argument in the "start" and * "end" event handlers. It is the third argument in the "complete", * "success", and "failure" event handlers. Be sure to quote * this property name in the transaction configuration as * "arguments" is a reserved word in JavaScript (e.g. * `Y.io({ ..., "arguments": stuff })`).
*
* * @method send * @public * @param {String} uri Qualified path to transaction resource. * @param {Object} config Configuration object for the transaction. * @param {Number} id Transaction id, if already set. * @return {Object} */ send: function(uri, config, id) { var transaction, method, i, len, sync, data, io = this, u = uri, response = {}; config = config ? Y.Object(config) : {}; transaction = io._create(config, id); method = config.method ? config.method.toUpperCase() : 'GET'; sync = config.sync; data = config.data; // Serialize an map object into a key-value string using // querystring-stringify-simple. if ((Y.Lang.isObject(data) && !data.nodeType) && !transaction.upload) { data = Y.QueryString.stringify(data); } if (config.form) { if (config.form.upload) { // This is a file upload transaction, calling // upload() in io-upload-iframe. return io.upload(transaction, uri, config); } else { // Serialize HTML form data into a key-value string. data = io._serialize(config.form, data); } } if (data) { switch (method) { case 'GET': case 'HEAD': case 'DELETE': u = io._concat(u, data); data = ''; break; case 'POST': case 'PUT': // If Content-Type is defined in the configuration object, or // or as a default header, it will be used instead of // 'application/x-www-form-urlencoded; charset=UTF-8' config.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, config.headers); break; } } if (transaction.xdr) { // Route data to io-xdr module for flash and XDomainRequest. return io.xdr(u, transaction, config); } else if (transaction.notify) { // Route data to custom transport return transaction.c.send(transaction, uri, config); } if (!sync && !transaction.upload) { transaction.c.onreadystatechange = function() { io._rS(transaction, config); }; } try { // Determine if request is to be set as // synchronous or asynchronous. transaction.c.open(method, u, !sync, config.username || null, config.password || null); io._setHeaders(transaction.c, config.headers || {}); io.start(transaction, config); // Will work only in browsers that implement the // Cross-Origin Resource Sharing draft. if (config.xdr && config.xdr.credentials) { if (!Y.UA.ie) { transaction.c.withCredentials = true; } } // Using "null" with HTTP POST will result in a request // with no Content-Length header defined. transaction.c.send(data); if (sync) { // Create a response object for synchronous transactions, // mixing id and arguments properties with the xhr // properties whitelist. for (i = 0, len = XHR_PROPS.length; i < len; ++i) { response[XHR_PROPS[i]] = transaction.c[XHR_PROPS[i]]; } response.getAllResponseHeaders = function() { return transaction.c.getAllResponseHeaders(); }; response.getResponseHeader = function(name) { return transaction.c.getResponseHeader(name); }; io.complete(transaction, config); io._result(transaction, config); return response; } } catch(e) { if (transaction.xdr) { // This exception is usually thrown by browsers // that do not support XMLHttpRequest Level 2. // Retry the request with the XDR transport set // to 'flash'. If the Flash transport is not // initialized or available, the transaction // will resolve to a transport error. return io._retry(transaction, uri, config); } else { io.complete(transaction, config); io._result(transaction, config); } } // If config.timeout is defined, and the request is standard XHR, // initialize timeout polling. if (config.timeout) { io._startTimeout(transaction, config.timeout); } return { id: transaction.id, abort: function() { return transaction.c ? io._abort(transaction, 'abort') : false; }, isInProgress: function() { return transaction.c ? (transaction.c.readyState % 4) : false; }, io: io }; } }; /** Method for initiating an ajax call. The first argument is the url end point for the call. The second argument is an object to configure the transaction and attach event subscriptions. The configuration object supports the following properties:
method
HTTP method verb (e.g., GET or POST). If this property is not not defined, the default value will be GET.
data
This is the name-value string that will be sent as the transaction data. If the request is HTTP GET, the data become part of querystring. If HTTP POST, the data are sent in the message body.
xdr
Defines the transport to be used for cross-domain requests. By setting this property, the transaction will use the specified transport instead of XMLHttpRequest. The properties of the transport object are:
use
The transport to be used: 'flash' or 'native'
dataType
Set the value to 'XML' if that is the expected response content type.
form
Form serialization configuration object. Its properties are:
id
Node object or id of HTML form
useDisabled
`true` to also serialize disabled form field values (defaults to `false`)
on
Assigns transaction event subscriptions. Available events are:
start
Fires when a request is sent to a resource.
complete
Fires when the transaction is complete.
success
Fires when the HTTP response status is within the 2xx range.
failure
Fires when the HTTP response status is outside the 2xx range, if an exception occurs, if the transation is aborted, or if the transaction exceeds a configured `timeout`.
end
Fires at the conclusion of the transaction lifecycle, after `success` or `failure`.

Callback functions for `start` and `end` receive the id of the transaction as a first argument. For `complete`, `success`, and `failure`, callbacks receive the id and the response object (usually the XMLHttpRequest instance). If the `arguments` property was included in the configuration object passed to `Y.io()`, the configured data will be passed to all callbacks as the last argument.

sync
Pass `true` to make a same-domain transaction synchronous. CAVEAT: This will negatively impact the user experience. Have a very good reason if you intend to use this.
context
The "`this'" object for all configured event handlers. If a specific context is needed for individual callbacks, bind the callback to a context using `Y.bind()`.
headers
Object map of transaction headers to send to the server. The object keys are the header names and the values are the header values.
timeout
Millisecond threshold for the transaction before being automatically aborted.
arguments
User-defined data passed to all registered event handlers. This value is available as the second argument in the "start" and "end" event handlers. It is the third argument in the "complete", "success", and "failure" event handlers. Be sure to quote this property name in the transaction configuration as "arguments" is a reserved word in JavaScript (e.g. `Y.io({ ..., "arguments": stuff })`).
@method io @static @param {String} url qualified path to transaction resource. @param {Object} config configuration object for the transaction. @return {Object} @for YUI **/ Y.io = function(url, config) { // Calling IO through the static interface will use and reuse // an instance of IO. var transaction = Y.io._map['io:0'] || new IO(); return transaction.send.apply(transaction, [url, config]); }; /** Method for setting and deleting IO HTTP headers to be sent with every request. Hosted as a property on the `io` function (e.g. `Y.io.header`). @method header @param {String} name HTTP header @param {String} value HTTP header value @static **/ Y.io.header = function(name, value) { // Calling IO through the static interface will use and reuse // an instance of IO. var transaction = Y.io._map['io:0'] || new IO(); transaction.setHeader(name, value); }; Y.IO = IO; // Map of all IO instances created. Y.io._map = {}; var XHR = win && win.XMLHttpRequest, XDR = win && win.XDomainRequest, AX = win && win.ActiveXObject; Y.mix(Y.IO, { /** * The ID of the default IO transport, defaults to `xhr` * @property _default * @type {String} * @static */ _default: 'xhr', /** * * @method defaultTransport * @static * @param {String} [id] The transport to set as the default, if empty a new transport is created. * @return {Object} The transport object with a `send` method */ defaultTransport: function(id) { if (id) { Y.IO._default = id; } else { var o = { c: Y.IO.transports[Y.IO._default](), notify: Y.IO._default === 'xhr' ? false : true }; return o; } }, /** * An object hash of custom transports available to IO * @property transports * @type {Object} * @static */ transports: { xhr: function () { return XHR ? new XMLHttpRequest() : AX ? new ActiveXObject('Microsoft.XMLHTTP') : null; }, xdr: function () { return XDR ? new XDomainRequest() : null; }, iframe: function () { return {}; }, flash: null, nodejs: null }, /** * Create a custom transport of type and return it's object * @method customTransport * @param {String} id The id of the transport to create. * @static */ customTransport: function(id) { var o = { c: Y.IO.transports[id]() }; o[(id === 'xdr' || id === 'flash') ? 'xdr' : 'notify'] = true; return o; } }); Y.mix(Y.IO.prototype, { /** * Fired from the notify method of the transport which in turn fires * the event on the IO object. * @method notify * @param {String} event The name of the event * @param {Object} transaction The transaction object * @param {Object} config The configuration object for this transaction */ notify: function(event, transaction, config) { var io = this; switch (event) { case 'timeout': case 'abort': case 'transport error': transaction.c = { status: 0, statusText: event }; event = 'failure'; default: io[event].apply(io, [transaction, config]); } } }); }, '3.5.1' ,{requires:['event-custom-base', 'querystring-stringify-simple']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('json-parse', function(Y) { /** *

The JSON module adds support for serializing JavaScript objects into * JSON strings and parsing JavaScript objects from strings in JSON format.

* *

The JSON namespace is added to your YUI instance including static methods * Y.JSON.parse(..) and Y.JSON.stringify(..).

* *

The functionality and method signatures follow the ECMAScript 5 * specification. In browsers with native JSON support, the native * implementation is used.

* *

The json module is a rollup of json-parse and * json-stringify.

* *

As their names suggest, json-parse adds support for parsing * JSON data (Y.JSON.parse) and json-stringify for serializing * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to * include either of the submodules individually if you don't need the * complementary functionality, or include the rollup for both.

* * @module json * @main json * @class JSON * @static */ /** * Provides Y.JSON.parse method to accept JSON strings and return native * JavaScript objects. * * @module json * @submodule json-parse * @for JSON * @static */ // All internals kept private for security reasons function fromGlobal(ref) { return (Y.config.win || this || {})[ref]; } /** * Alias to native browser implementation of the JSON object if available. * * @property Native * @type {Object} * @private */ var _JSON = fromGlobal('JSON'), Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON), useNative = !!Native, /** * Replace certain Unicode characters that JavaScript may handle incorrectly * during eval--either by deleting them or treating them as line * endings--with escape sequences. * IMPORTANT NOTE: This regex will be used to modify the input if a match is * found. * * @property _UNICODE_EXCEPTIONS * @type {RegExp} * @private */ _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, /** * First step in the safety evaluation. Regex used to replace all escape * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character). * * @property _ESCAPES * @type {RegExp} * @private */ _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, /** * Second step in the safety evaluation. Regex used to replace all simple * values with ']' characters. * * @property _VALUES * @type {RegExp} * @private */ _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, /** * Third step in the safety evaluation. Regex used to remove all open * square brackets following a colon, comma, or at the beginning of the * string. * * @property _BRACKETS * @type {RegExp} * @private */ _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g, /** * Final step in the safety evaluation. Regex used to test the string left * after all previous replacements for invalid characters. * * @property _UNSAFE * @type {RegExp} * @private */ _UNSAFE = /[^\],:{}\s]/, /** * Replaces specific unicode characters with their appropriate \unnnn * format. Some browsers ignore certain characters during eval. * * @method escapeException * @param c {String} Unicode character * @return {String} the \unnnn escapement of the character * @private */ _escapeException = function (c) { return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4); }, /** * Traverses nested objects, applying a reviver function to each (key,value) * from the scope if the key:value's containing object. The value returned * from the function will replace the original value in the key:value pair. * If the value returned is undefined, the key will be omitted from the * returned object. * * @method _revive * @param data {MIXED} Any JavaScript data * @param reviver {Function} filter or mutation function * @return {MIXED} The results of the filtered data * @private */ _revive = function (data, reviver) { var walk = function (o,key) { var k,v,value = o[key]; if (value && typeof value === 'object') { for (k in value) { if (value.hasOwnProperty(k)) { v = walk(value, k); if (v === undefined) { delete value[k]; } else { value[k] = v; } } } } return reviver.call(o,key,value); }; return typeof reviver === 'function' ? walk({'':data},'') : data; }, /** * Parse a JSON string, returning the native JavaScript representation. * * @param s {string} JSON string data * @param reviver {function} (optional) function(k,v) passed each key value * pair of object literals, allowing pruning or altering values * @return {MIXED} the native JavaScript representation of the JSON string * @throws SyntaxError * @method parse * @static */ // JavaScript implementation in lieu of native browser support. Based on // the json2.js library from http://json.org _parse = function (s,reviver) { // Replace certain Unicode characters that are otherwise handled // incorrectly by some browser implementations. // NOTE: This modifies the input if such characters are found! s = s.replace(_UNICODE_EXCEPTIONS, _escapeException); // Test for any remaining invalid characters if (!_UNSAFE.test(s.replace(_ESCAPES,'@'). replace(_VALUES,']'). replace(_BRACKETS,''))) { // Eval the text into a JavaScript data structure, apply any // reviver function, and return return _revive( eval('(' + s + ')'), reviver ); } throw new SyntaxError('JSON.parse'); }; Y.namespace('JSON').parse = function (s,reviver) { if (typeof s !== 'string') { s += ''; } return Native && Y.JSON.useNativeParse ? Native.parse(s,reviver) : _parse(s,reviver); }; function workingNative( k, v ) { return k === "ok" ? true : v; } // Double check basic functionality. This is mainly to catch early broken // implementations of the JSON API in Firefox 3.1 beta1 and beta2 if ( Native ) { try { useNative = ( Native.parse( '{"ok":false}', workingNative ) ).ok; } catch ( e ) { useNative = false; } } /** * Leverage native JSON parse if the browser has a native implementation. * In general, this is a good idea. See the Known Issues section in the * JSON user guide for caveats. The default value is true for browsers with * native JSON support. * * @property useNativeParse * @type Boolean * @default true * @static */ Y.JSON.useNativeParse = useNative; }, '3.5.1' ,{requires:['yui-base']}); YUI.add('moodle-enrol-notification', function(Y) { var DIALOGUE_NAME = 'Moodle dialogue', DIALOGUE_PREFIX = 'moodle-dialogue', CONFIRM_NAME = 'Moodle confirmation dialogue', EXCEPTION_NAME = 'Moodle exception', AJAXEXCEPTION_NAME = 'Moodle AJAX exception', ALERT_NAME = 'Moodle alert', C = Y.Node.create, BASE = 'notificationBase', LIGHTBOX = 'lightbox', NODELIGHTBOX = 'nodeLightbox', COUNT = 0, CONFIRMYES = 'yesLabel', CONFIRMNO = 'noLabel', TITLE = 'title', QUESTION = 'question', CSS = { BASE : 'moodle-dialogue-base', WRAP : 'moodle-dialogue-wrap', HEADER : 'moodle-dialogue-hd', BODY : 'moodle-dialogue-bd', CONTENT : 'moodle-dialogue-content', FOOTER : 'moodle-dialogue-ft', HIDDEN : 'hidden', LIGHTBOX : 'moodle-dialogue-lightbox' }; var DIALOGUE = function(config) { COUNT++; var id = 'moodle-dialogue-'+COUNT; config.notificationBase = C('
') .append(C('
')) .append(C('
') .append(C('
')) .append(C('
')) .append(C('
'))); Y.one(document.body).append(config.notificationBase); config.srcNode = '#'+id; config.width = config.width || '400px'; config.visible = config.visible || false; config.center = config.centered || true; config.centered = false; DIALOGUE.superclass.constructor.apply(this, [config]); }; Y.extend(DIALOGUE, Y.Overlay, { initializer : function(config) { this.set(NODELIGHTBOX, this.get(BASE).one('.'+CSS.LIGHTBOX).setStyle('opacity', 0.5)); this.after('visibleChange', this.visibilityChanged, this); this.after('headerContentChange', function(e){ var h = (this.get('closeButton'))?this.get(BASE).one('.'+CSS.HEADER):false; if (h && !h.one('.closebutton')) { var c = C('
'); c.on('click', this.hide, this); h.append(c); } }, this); this.render(); this.show(); }, visibilityChanged : function(e) { switch (e.attrName) { case 'visible': if (this.get(LIGHTBOX)) { var l = this.get(NODELIGHTBOX); if (!e.prevVal && e.newVal) { l.setStyle('height',l.get('docHeight')+'px').removeClass(CSS.HIDDEN); } else if (e.prevVal && !e.newVal) { l.addClass(CSS.HIDDEN); } } if (this.get('center') && !e.prevVal && e.newVal) { this.centerDialogue(); } if (this.get('draggable')) { var titlebar = '#' + this.get('id') + ' .' + CSS.HEADER; this.plug(Y.Plugin.Drag, {handles : [titlebar]}); this.dd.addInvalid('div.closebutton'); Y.one(titlebar).setStyle('cursor', 'move'); } break; } }, centerDialogue : function() { var bb = this.get('boundingBox'), hidden = bb.hasClass(DIALOGUE_PREFIX+'-hidden'); if (hidden) { bb.setStyle('top', '-1000px').removeClass(DIALOGUE_PREFIX+'-hidden'); } var x = Math.max(Math.round((bb.get('winWidth') - bb.get('offsetWidth'))/2), 15); var y = Math.max(Math.round((bb.get('winHeight') - bb.get('offsetHeight'))/2), 15) + Y.one(window).get('scrollTop'); if (hidden) { bb.addClass(DIALOGUE_PREFIX+'-hidden'); } bb.setStyle('left', x).setStyle('top', y); } }, { NAME : DIALOGUE_NAME, CSS_PREFIX : DIALOGUE_PREFIX, ATTRS : { notificationBase : { }, nodeLightbox : { value : null }, lightbox : { validator : Y.Lang.isBoolean, value : true }, closeButton : { validator : Y.Lang.isBoolean, value : true }, center : { validator : Y.Lang.isBoolean, value : true }, draggable : { validator : Y.Lang.isBoolean, value : false } } }); var ALERT = function(config) { config.closeButton = false; ALERT.superclass.constructor.apply(this, [config]); }; Y.extend(ALERT, DIALOGUE, { _enterKeypress : null, initializer : function(config) { this.publish('complete'); var yes = C(''), content = C('
') .append(C('
'+this.get('message')+'
')) .append(C('
') .append(yes)); this.get(BASE).addClass('moodle-dialogue-confirm'); this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); this.setStdModContent(Y.WidgetStdMod.HEADER, this.get(TITLE), Y.WidgetStdMod.REPLACE); this.after('destroyedChange', function(){this.get(BASE).remove();}, this); this._enterKeypress = Y.on('key', this.submit, window, 'down:13', this); yes.on('click', this.submit, this); }, submit : function(e, outcome) { this._enterKeypress.detach(); this.fire('complete'); this.hide(); this.destroy(); } }, { NAME : ALERT_NAME, CSS_PREFIX : DIALOGUE_PREFIX, ATTRS : { title : { validator : Y.Lang.isString, value : 'Alert' }, message : { validator : Y.Lang.isString, value : 'Confirm' }, yesLabel : { validator : Y.Lang.isString, setter : function(txt) { if (!txt) { txt = 'Ok'; } return txt; }, value : 'Ok' } } }); var CONFIRM = function(config) { CONFIRM.superclass.constructor.apply(this, [config]); }; Y.extend(CONFIRM, DIALOGUE, { _enterKeypress : null, _escKeypress : null, initializer : function(config) { this.publish('complete'); this.publish('complete-yes'); this.publish('complete-no'); var yes = C(''), no = C(''), content = C('
') .append(C('
'+this.get(QUESTION)+'
')) .append(C('
') .append(yes) .append(no)); this.get(BASE).addClass('moodle-dialogue-confirm'); this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); this.setStdModContent(Y.WidgetStdMod.HEADER, this.get(TITLE), Y.WidgetStdMod.REPLACE); this.after('destroyedChange', function(){this.get(BASE).remove();}, this); this._enterKeypress = Y.on('key', this.submit, window, 'down:13', this, true); this._escKeypress = Y.on('key', this.submit, window, 'down:27', this, false); yes.on('click', this.submit, this, true); no.on('click', this.submit, this, false); }, submit : function(e, outcome) { this._enterKeypress.detach(); this._escKeypress.detach(); this.fire('complete', outcome); if (outcome) { this.fire('complete-yes'); } else { this.fire('complete-no'); } this.hide(); this.destroy(); } }, { NAME : CONFIRM_NAME, CSS_PREFIX : DIALOGUE_PREFIX, ATTRS : { yesLabel : { validator : Y.Lang.isString, value : 'Yes' }, noLabel : { validator : Y.Lang.isString, value : 'No' }, title : { validator : Y.Lang.isString, value : 'Confirm' }, question : { validator : Y.Lang.isString, value : 'Are you sure?' } } }); Y.augment(CONFIRM, Y.EventTarget); var EXCEPTION = function(config) { config.width = config.width || (M.cfg.developerdebug)?Math.floor(Y.one(document.body).get('winWidth')/3)+'px':null; config.closeButton = true; EXCEPTION.superclass.constructor.apply(this, [config]); }; Y.extend(EXCEPTION, DIALOGUE, { _hideTimeout : null, _keypress : null, initializer : function(config) { this.get(BASE).addClass('moodle-dialogue-exception'); this.setStdModContent(Y.WidgetStdMod.HEADER, config.name, Y.WidgetStdMod.REPLACE); var content = C('
') .append(C('
'+this.get('message')+'
')) .append(C('')) .append(C('')) .append(C('')); if (M.cfg.developerdebug) { content.all('.moodle-exception-param').removeClass('hidden'); } this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); var self = this; var delay = this.get('hideTimeoutDelay'); if (delay) { this._hideTimeout = setTimeout(function(){self.hide();}, delay); } this.after('visibleChange', this.visibilityChanged, this); this.after('destroyedChange', function(){this.get(BASE).remove();}, this); this._keypress = Y.on('key', this.hide, window, 'down:13,27', this); this.centerDialogue(); }, visibilityChanged : function(e) { if (e.attrName == 'visible' && e.prevVal && !e.newVal) { if (this._keypress) this._keypress.detach(); var self = this; setTimeout(function(){self.destroy();}, 1000); } } }, { NAME : EXCEPTION_NAME, CSS_PREFIX : DIALOGUE_PREFIX, ATTRS : { message : { value : '' }, name : { value : '' }, fileName : { value : '' }, lineNumber : { value : '' }, stack : { setter : function(str) { var lines = str.split("\n"); var pattern = new RegExp('^(.+)@('+M.cfg.wwwroot+')?(.{0,75}).*:(\\d+)$'); for (var i in lines) { lines[i] = lines[i].replace(pattern, "
ln: $4
$3
$1
"); } return lines.join(''); }, value : '' }, hideTimeoutDelay : { validator : Y.Lang.isNumber, value : null } } }); var AJAXEXCEPTION = function(config) { config.name = config.name || 'Error'; config.closeButton = true; AJAXEXCEPTION.superclass.constructor.apply(this, [config]); }; Y.extend(AJAXEXCEPTION, DIALOGUE, { _keypress : null, initializer : function(config) { this.get(BASE).addClass('moodle-dialogue-exception'); this.setStdModContent(Y.WidgetStdMod.HEADER, config.name, Y.WidgetStdMod.REPLACE); var content = C('
') .append(C('
'+this.get('error')+'
')) .append(C('')) .append(C('')) .append(C('')); if (M.cfg.developerdebug) { content.all('.moodle-exception-param').removeClass('hidden'); } this.setStdModContent(Y.WidgetStdMod.BODY, content, Y.WidgetStdMod.REPLACE); var self = this; var delay = this.get('hideTimeoutDelay'); if (delay) { this._hideTimeout = setTimeout(function(){self.hide();}, delay); } this.after('visibleChange', this.visibilityChanged, this); this._keypress = Y.on('key', this.hide, window, 'down:13, 27', this); this.centerDialogue(); }, visibilityChanged : function(e) { if (e.attrName == 'visible' && e.prevVal && !e.newVal) { var self = this; this._keypress.detach(); setTimeout(function(){self.destroy();}, 1000); } } }, { NAME : AJAXEXCEPTION_NAME, CSS_PREFIX : DIALOGUE_PREFIX, ATTRS : { error : { validator : Y.Lang.isString, value : 'Unknown error' }, debuginfo : { value : null }, stacktrace : { value : null }, reproductionlink : { setter : function(link) { if (link !== null) { link = ''+link.replace(M.cfg.wwwroot, '')+''; } return link; }, value : null }, hideTimeoutDelay : { validator : Y.Lang.isNumber, value : null } } }); M.core = M.core || {}; M.core.dialogue = DIALOGUE; M.core.alert = ALERT; M.core.confirm = CONFIRM; M.core.exception = EXCEPTION; M.core.ajaxException = AJAXEXCEPTION; }, '@VERSION@', {requires:['base','node','overlay','event-key', 'moodle-enrol-notification-skin', 'dd-plugin']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('classnamemanager', function(Y) { /** * Contains a singleton (ClassNameManager) that enables easy creation and caching of * prefixed class names. * @module classnamemanager */ /** * A singleton class providing: * * * * @class ClassNameManager * @static */ // String constants var CLASS_NAME_PREFIX = 'classNamePrefix', CLASS_NAME_DELIMITER = 'classNameDelimiter', CONFIG = Y.config; // Global config /** * Configuration property indicating the prefix for all CSS class names in this YUI instance. * * @property classNamePrefix * @type {String} * @default "yui" * @static */ CONFIG[CLASS_NAME_PREFIX] = CONFIG[CLASS_NAME_PREFIX] || 'yui3'; /** * Configuration property indicating the delimiter used to compose all CSS class names in * this YUI instance. * * @property classNameDelimiter * @type {String} * @default "-" * @static */ CONFIG[CLASS_NAME_DELIMITER] = CONFIG[CLASS_NAME_DELIMITER] || '-'; Y.ClassNameManager = function () { var sPrefix = CONFIG[CLASS_NAME_PREFIX], sDelimiter = CONFIG[CLASS_NAME_DELIMITER]; return { /** * Returns a class name prefixed with the the value of the * Y.config.classNamePrefix attribute + the provided strings. * Uses the Y.config.classNameDelimiter attribute to delimit the * provided strings. E.g. Y.ClassNameManager.getClassName('foo','bar'); // yui-foo-bar * * @method getClassName * @param {String}+ classnameSection one or more classname sections to be joined * @param {Boolean} skipPrefix If set to true, the classname will not be prefixed with the default Y.config.classNameDelimiter value. */ getClassName: Y.cached(function () { var args = Y.Array(arguments); if (args[args.length-1] !== true) { args.unshift(sPrefix); } else { args.pop(); } return args.join(sDelimiter); }) }; }(); }, '3.5.1' ,{requires:['yui-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('event-synthetic', function(Y) { /** * Define new DOM events that can be subscribed to from Nodes. * * @module event * @submodule event-synthetic */ var DOMMap = Y.Env.evt.dom_map, toArray = Y.Array, YLang = Y.Lang, isObject = YLang.isObject, isString = YLang.isString, isArray = YLang.isArray, query = Y.Selector.query, noop = function () {}; /** *

The triggering mechanism used by SyntheticEvents.

* *

Implementers should not instantiate these directly. Use the Notifier * provided to the event's implemented on(node, sub, notifier) or * delegate(node, sub, notifier, filter) methods.

* * @class SyntheticEvent.Notifier * @constructor * @param handle {EventHandle} the detach handle for the subscription to an * internal custom event used to execute the callback passed to * on(..) or delegate(..) * @param emitFacade {Boolean} take steps to ensure the first arg received by * the subscription callback is an event facade * @private * @since 3.2.0 */ function Notifier(handle, emitFacade) { this.handle = handle; this.emitFacade = emitFacade; } /** *

Executes the subscription callback, passing the firing arguments as the * first parameters to that callback. For events that are configured with * emitFacade=true, it is common practice to pass the triggering DOMEventFacade * as the first parameter. Barring a proper DOMEventFacade or EventFacade * (from a CustomEvent), a new EventFacade will be generated. In that case, if * fire() is called with a simple object, it will be mixed into the facade. * Otherwise, the facade will be prepended to the callback parameters.

* *

For notifiers provided to delegate logic, the first argument should be an * object with a "currentTarget" property to identify what object to * default as 'this' in the callback. Typically this is gleaned from the * DOMEventFacade or EventFacade, but if configured with emitFacade=false, an * object must be provided. In that case, the object will be removed from the * callback parameters.

* *

Additional arguments passed during event subscription will be * automatically added after those passed to fire().

* * @method fire * @param e {EventFacade|DOMEventFacade|Object|any} (see description) * @param arg* {any} additional arguments received by all subscriptions * @private */ Notifier.prototype.fire = function (e) { // first arg to delegate notifier should be an object with currentTarget var args = toArray(arguments, 0, true), handle = this.handle, ce = handle.evt, sub = handle.sub, thisObj = sub.context, delegate = sub.filter, event = e || {}, ret; if (this.emitFacade) { if (!e || !e.preventDefault) { event = ce._getFacade(); if (isObject(e) && !e.preventDefault) { Y.mix(event, e, true); args[0] = event; } else { args.unshift(event); } } event.type = ce.type; event.details = args.slice(); if (delegate) { event.container = ce.host; } } else if (delegate && isObject(e) && e.currentTarget) { args.shift(); } sub.context = thisObj || event.currentTarget || ce.host; ret = ce.fire.apply(ce, args); sub.context = thisObj; // reset for future firing // to capture callbacks that return false to stopPropagation. // Useful for delegate implementations return ret; }; /** * Manager object for synthetic event subscriptions to aggregate multiple synths on the same node without colliding with actual DOM subscription entries in the global map of DOM subscriptions. Also facilitates proper cleanup on page unload. * * @class SynthRegistry * @constructor * @param el {HTMLElement} the DOM element * @param yuid {String} the yuid stamp for the element * @param key {String} the generated id token used to identify an event type + * element in the global DOM subscription map. * @private */ function SynthRegistry(el, yuid, key) { this.handles = []; this.el = el; this.key = key; this.domkey = yuid; } SynthRegistry.prototype = { constructor: SynthRegistry, // A few object properties to fake the CustomEvent interface for page // unload cleanup. DON'T TOUCH! type : '_synth', fn : noop, capture : false, /** * Adds a subscription from the Notifier registry. * * @method register * @param handle {EventHandle} the subscription * @since 3.4.0 */ register: function (handle) { handle.evt.registry = this; this.handles.push(handle); }, /** * Removes the subscription from the Notifier registry. * * @method _unregisterSub * @param sub {Subscription} the subscription * @since 3.4.0 */ unregister: function (sub) { var handles = this.handles, events = DOMMap[this.domkey], i; for (i = handles.length - 1; i >= 0; --i) { if (handles[i].sub === sub) { handles.splice(i, 1); break; } } // Clean up left over objects when there are no more subscribers. if (!handles.length) { delete events[this.key]; if (!Y.Object.size(events)) { delete DOMMap[this.domkey]; } } }, /** * Used by the event system's unload cleanup process. When navigating * away from the page, the event system iterates the global map of element * subscriptions and detaches everything using detachAll(). Normally, * the map is populated with custom events, so this object needs to * at least support the detachAll method to duck type its way to * cleanliness. * * @method detachAll * @private * @since 3.4.0 */ detachAll : function () { var handles = this.handles, i = handles.length; while (--i >= 0) { handles[i].detach(); } } }; /** *

Wrapper class for the integration of new events into the YUI event * infrastructure. Don't instantiate this object directly, use * Y.Event.define(type, config). See that method for details.

* *

Properties that MAY or SHOULD be specified in the configuration are noted * below and in the description of Y.Event.define.

* * @class SyntheticEvent * @constructor * @param cfg {Object} Implementation pieces and configuration * @since 3.1.0 * @in event-synthetic */ function SyntheticEvent() { this._init.apply(this, arguments); } Y.mix(SyntheticEvent, { Notifier: Notifier, SynthRegistry: SynthRegistry, /** * Returns the array of subscription handles for a node for the given event * type. Passing true as the third argument will create a registry entry * in the event system's DOM map to host the array if one doesn't yet exist. * * @method getRegistry * @param node {Node} the node * @param type {String} the event * @param create {Boolean} create a registration entry to host a new array * if one doesn't exist. * @return {Array} * @static * @protected * @since 3.2.0 */ getRegistry: function (node, type, create) { var el = node._node, yuid = Y.stamp(el), key = 'event:' + yuid + type + '_synth', events = DOMMap[yuid]; if (create) { if (!events) { events = DOMMap[yuid] = {}; } if (!events[key]) { events[key] = new SynthRegistry(el, yuid, key); } } return (events && events[key]) || null; }, /** * Alternate _delete() method for the CustomEvent object * created to manage SyntheticEvent subscriptions. * * @method _deleteSub * @param sub {Subscription} the subscription to clean up * @private * @since 3.2.0 */ _deleteSub: function (sub) { if (sub && sub.fn) { var synth = this.eventDef, method = (sub.filter) ? 'detachDelegate' : 'detach'; this.subscribers = {}; this.subCount = 0; synth[method](sub.node, sub, this.notifier, sub.filter); this.registry.unregister(sub); delete sub.fn; delete sub.node; delete sub.context; } }, prototype: { constructor: SyntheticEvent, /** * Construction logic for the event. * * @method _init * @protected */ _init: function () { var config = this.publishConfig || (this.publishConfig = {}); // The notification mechanism handles facade creation this.emitFacade = ('emitFacade' in config) ? config.emitFacade : true; config.emitFacade = false; }, /** *

Implementers MAY provide this method definition.

* *

Implement this function if the event supports a different * subscription signature. This function is used by both * on() and delegate(). The second parameter * indicates that the event is being subscribed via * delegate().

* *

Implementations must remove extra arguments from the args list * before returning. The required args for on() * subscriptions are

*
[type, callback, target, context, argN...]
* *

The required args for delegate() * subscriptions are

* *
[type, callback, target, filter, context, argN...]
* *

The return value from this function will be stored on the * subscription in the '_extra' property for reference elsewhere.

* * @method processArgs * @param args {Array} parmeters passed to Y.on(..) or Y.delegate(..) * @param delegate {Boolean} true if the subscription is from Y.delegate * @return {any} */ processArgs: noop, /** *

Implementers MAY override this property.

* *

Whether to prevent multiple subscriptions to this event that are * classified as being the same. By default, this means the subscribed * callback is the same function. See the subMatch * method. Setting this to true will impact performance for high volume * events.

* * @property preventDups * @type {Boolean} * @default false */ //preventDups : false, /** *

Implementers SHOULD provide this method definition.

* * Implementation logic for subscriptions done via node.on(type, * fn) or Y.on(type, fn, target). This * function should set up the monitor(s) that will eventually fire the * event. Typically this involves subscribing to at least one DOM * event. It is recommended to store detach handles from any DOM * subscriptions to make for easy cleanup in the detach * method. Typically these handles are added to the sub * object. Also for SyntheticEvents that leverage a single DOM * subscription under the hood, it is recommended to pass the DOM event * object to notifier.fire(e). (The event name on the * object will be updated). * * @method on * @param node {Node} the node the subscription is being applied to * @param sub {Subscription} the object to track this subscription * @param notifier {SyntheticEvent.Notifier} call notifier.fire(..) to * trigger the execution of the subscribers */ on: noop, /** *

Implementers SHOULD provide this method definition.

* *

Implementation logic for detaching subscriptions done via * node.on(type, fn). This function should clean up any * subscriptions made in the on() phase.

* * @method detach * @param node {Node} the node the subscription was applied to * @param sub {Subscription} the object tracking this subscription * @param notifier {SyntheticEvent.Notifier} the Notifier used to * trigger the execution of the subscribers */ detach: noop, /** *

Implementers SHOULD provide this method definition.

* *

Implementation logic for subscriptions done via * node.delegate(type, fn, filter) or * Y.delegate(type, fn, container, filter). Like with * on() above, this function should monitor the environment * for the event being fired, and trigger subscription execution by * calling notifier.fire(e).

* *

This function receives a fourth argument, which is the filter * used to identify which Node's are of interest to the subscription. * The filter will be either a boolean function that accepts a target * Node for each hierarchy level as the event bubbles, or a selector * string. To translate selector strings into filter functions, use * Y.delegate.compileFilter(filter).

* * @method delegate * @param node {Node} the node the subscription is being applied to * @param sub {Subscription} the object to track this subscription * @param notifier {SyntheticEvent.Notifier} call notifier.fire(..) to * trigger the execution of the subscribers * @param filter {String|Function} Selector string or function that * accepts an event object and returns null, a Node, or an * array of Nodes matching the criteria for processing. * @since 3.2.0 */ delegate : noop, /** *

Implementers SHOULD provide this method definition.

* *

Implementation logic for detaching subscriptions done via * node.delegate(type, fn, filter) or * Y.delegate(type, fn, container, filter). This function * should clean up any subscriptions made in the * delegate() phase.

* * @method detachDelegate * @param node {Node} the node the subscription was applied to * @param sub {Subscription} the object tracking this subscription * @param notifier {SyntheticEvent.Notifier} the Notifier used to * trigger the execution of the subscribers * @param filter {String|Function} Selector string or function that * accepts an event object and returns null, a Node, or an * array of Nodes matching the criteria for processing. * @since 3.2.0 */ detachDelegate : noop, /** * Sets up the boilerplate for detaching the event and facilitating the * execution of subscriber callbacks. * * @method _on * @param args {Array} array of arguments passed to * Y.on(...) or Y.delegate(...) * @param delegate {Boolean} true if called from * Y.delegate(...) * @return {EventHandle} the detach handle for this subscription * @private * since 3.2.0 */ _on: function (args, delegate) { var handles = [], originalArgs = args.slice(), extra = this.processArgs(args, delegate), selector = args[2], method = delegate ? 'delegate' : 'on', nodes, handle; // Can't just use Y.all because it doesn't support window (yet?) nodes = (isString(selector)) ? query(selector) : toArray(selector || Y.one(Y.config.win)); if (!nodes.length && isString(selector)) { handle = Y.on('available', function () { Y.mix(handle, Y[method].apply(Y, originalArgs), true); }, selector); return handle; } Y.Array.each(nodes, function (node) { var subArgs = args.slice(), filter; node = Y.one(node); if (node) { if (delegate) { filter = subArgs.splice(3, 1)[0]; } // (type, fn, el, thisObj, ...) => (fn, thisObj, ...) subArgs.splice(0, 4, subArgs[1], subArgs[3]); if (!this.preventDups || !this.getSubs(node, args, null, true)) { handles.push(this._subscribe(node, method, subArgs, extra, filter)); } } }, this); return (handles.length === 1) ? handles[0] : new Y.EventHandle(handles); }, /** * Creates a new Notifier object for use by this event's * on(...) or delegate(...) implementation * and register the custom event proxy in the DOM system for cleanup. * * @method _subscribe * @param node {Node} the Node hosting the event * @param method {String} "on" or "delegate" * @param args {Array} the subscription arguments passed to either * Y.on(...) or Y.delegate(...) * after running through processArgs(args) to * normalize the argument signature * @param extra {any} Extra data parsed from * processArgs(args) * @param filter {String|Function} the selector string or function * filter passed to Y.delegate(...) (not * present when called from Y.on(...)) * @return {EventHandle} * @private * @since 3.2.0 */ _subscribe: function (node, method, args, extra, filter) { var dispatcher = new Y.CustomEvent(this.type, this.publishConfig), handle = dispatcher.on.apply(dispatcher, args), notifier = new Notifier(handle, this.emitFacade), registry = SyntheticEvent.getRegistry(node, this.type, true), sub = handle.sub; sub.node = node; sub.filter = filter; if (extra) { this.applyArgExtras(extra, sub); } Y.mix(dispatcher, { eventDef : this, notifier : notifier, host : node, // I forget what this is for currentTarget: node, // for generating facades target : node, // for generating facades el : node._node, // For category detach _delete : SyntheticEvent._deleteSub }, true); handle.notifier = notifier; registry.register(handle); // Call the implementation's "on" or "delegate" method this[method](node, sub, notifier, filter); return handle; }, /** *

Implementers MAY provide this method definition.

* *

Implement this function if you want extra data extracted during * processArgs to be propagated to subscriptions on a per-node basis. * That is to say, if you call Y.on('xyz', fn, xtra, 'div') * the data returned from processArgs will be shared * across the subscription objects for all the divs. If you want each * subscription to receive unique information, do that processing * here.

* *

The default implementation adds the data extracted by processArgs * to the subscription object as sub._extra.

* * @method applyArgExtras * @param extra {any} Any extra data extracted from processArgs * @param sub {Subscription} the individual subscription */ applyArgExtras: function (extra, sub) { sub._extra = extra; }, /** * Removes the subscription(s) from the internal subscription dispatch * mechanism. See SyntheticEvent._deleteSub. * * @method _detach * @param args {Array} The arguments passed to * node.detach(...) * @private * @since 3.2.0 */ _detach: function (args) { // Can't use Y.all because it doesn't support window (yet?) // TODO: Does Y.all support window now? var target = args[2], els = (isString(target)) ? query(target) : toArray(target), node, i, len, handles, j; // (type, fn, el, context, filter?) => (type, fn, context, filter?) args.splice(2, 1); for (i = 0, len = els.length; i < len; ++i) { node = Y.one(els[i]); if (node) { handles = this.getSubs(node, args); if (handles) { for (j = handles.length - 1; j >= 0; --j) { handles[j].detach(); } } } } }, /** * Returns the detach handles of subscriptions on a node that satisfy a * search/filter function. By default, the filter used is the * subMatch method. * * @method getSubs * @param node {Node} the node hosting the event * @param args {Array} the array of original subscription args passed * to Y.on(...) (before * processArgs * @param filter {Function} function used to identify a subscription * for inclusion in the returned array * @param first {Boolean} stop after the first match (used to check for * duplicate subscriptions) * @return {EventHandle[]} detach handles for the matching subscriptions */ getSubs: function (node, args, filter, first) { var registry = SyntheticEvent.getRegistry(node, this.type), handles = [], allHandles, i, len, handle; if (registry) { allHandles = registry.handles; if (!filter) { filter = this.subMatch; } for (i = 0, len = allHandles.length; i < len; ++i) { handle = allHandles[i]; if (filter.call(this, handle.sub, args)) { if (first) { return handle; } else { handles.push(allHandles[i]); } } } } return handles.length && handles; }, /** *

Implementers MAY override this to define what constitutes a * "same" subscription. Override implementations should * consider the lack of a comparator as a match, so calling * getSubs() with no arguments will return all subs.

* *

Compares a set of subscription arguments against a Subscription * object to determine if they match. The default implementation * compares the callback function against the second argument passed to * Y.on(...) or node.detach(...) etc.

* * @method subMatch * @param sub {Subscription} the existing subscription * @param args {Array} the calling arguments passed to * Y.on(...) etc. * @return {Boolean} true if the sub can be described by the args * present * @since 3.2.0 */ subMatch: function (sub, args) { // Default detach cares only about the callback matching return !args[1] || sub.fn === args[1]; } } }, true); Y.SyntheticEvent = SyntheticEvent; /** *

Defines a new event in the DOM event system. Implementers are * responsible for monitoring for a scenario whereby the event is fired. A * notifier object is provided to the functions identified below. When the * criteria defining the event are met, call notifier.fire( [args] ); to * execute event subscribers.

* *

The first parameter is the name of the event. The second parameter is a * configuration object which define the behavior of the event system when the * new event is subscribed to or detached from. The methods that should be * defined in this configuration object are on, * detach, delegate, and detachDelegate. * You are free to define any other methods or properties needed to define your * event. Be aware, however, that since the object is used to subclass * SyntheticEvent, you should avoid method names used by SyntheticEvent unless * your intention is to override the default behavior.

* *

This is a list of properties and methods that you can or should specify * in the configuration object:

* *
*
on
*
function (node, subscription, notifier) The * implementation logic for subscription. Any special setup you need to * do to create the environment for the event being fired--E.g. native * DOM event subscriptions. Store subscription related objects and * state on the subscription object. When the * criteria have been met to fire the synthetic event, call * notifier.fire(e). See Notifier's fire() * method for details about what to pass as parameters.
* *
detach
*
function (node, subscription, notifier) The * implementation logic for cleaning up a detached subscription. E.g. * detach any DOM subscriptions added in on.
* *
delegate
*
function (node, subscription, notifier, filter) The * implementation logic for subscription via Y.delegate or * node.delegate. The filter is typically either a selector * string or a function. You can use * Y.delegate.compileFilter(selectorString) to create a * filter function from a selector string if needed. The filter function * expects an event object as input and should output either null, a * matching Node, or an array of matching Nodes. Otherwise, this acts * like on DOM event subscriptions. Store subscription * related objects and information on the subscription * object. When the criteria have been met to fire the synthetic event, * call notifier.fire(e) as noted above.
* *
detachDelegate
*
function (node, subscription, notifier) The * implementation logic for cleaning up a detached delegate subscription. * E.g. detach any DOM delegate subscriptions added in * delegate.
* *
publishConfig
*
(Object) The configuration object that will be used to instantiate * the underlying CustomEvent. See Notifier's fire method * for details.
* *
processArgs
*

function (argArray, fromDelegate) Optional method * to extract any additional arguments from the subscription * signature. Using this allows on or * delegate signatures like * node.on("hover", overCallback, * outCallback).

*

When processing an atypical argument signature, make sure the * args array is returned to the normal signature before returning * from the function. For example, in the "hover" example * above, the outCallback needs to be spliced * out of the array. The expected signature of the args array for * on() subscriptions is:

*
 *              [type, callback, target, contextOverride, argN...]
 *          
*

And for delegate():

*
 *              [type, callback, target, filter, contextOverride, argN...]
 *          
*

where target is the node the event is being * subscribed for. You can see these signatures documented for * Y.on() and Y.delegate() respectively.

*

Whatever gets returned from the function will be stored on the * subscription object under * subscription._extra.

*
subMatch
*
*

function (sub, args) Compares a set of * subscription arguments against a Subscription object to determine * if they match. The default implementation compares the callback * function against the second argument passed to * Y.on(...) or node.detach(...) etc.

*
*
* * @method define * @param type {String} the name of the event * @param config {Object} the prototype definition for the new event (see above) * @param force {Boolean} override an existing event (use with caution) * @return {SyntheticEvent} the subclass implementation instance created to * handle event subscriptions of this type * @static * @for Event * @since 3.1.0 * @in event-synthetic */ Y.Event.define = function (type, config, force) { var eventDef, Impl, synth; if (type && type.type) { eventDef = type; force = config; } else if (config) { eventDef = Y.merge({ type: type }, config); } if (eventDef) { if (force || !Y.Node.DOM_EVENTS[eventDef.type]) { Impl = function () { SyntheticEvent.apply(this, arguments); }; Y.extend(Impl, SyntheticEvent, eventDef); synth = new Impl(); type = synth.type; Y.Node.DOM_EVENTS[type] = Y.Env.evt.plugins[type] = { eventDef: synth, on: function () { return synth._on(toArray(arguments)); }, delegate: function () { return synth._on(toArray(arguments), true); }, detach: function () { return synth._detach(toArray(arguments)); } }; } } else if (isString(type) || isArray(type)) { Y.Array.each(toArray(type), function (t) { Y.Node.DOM_EVENTS[t] = 1; }); } return synth; }; }, '3.5.1' ,{requires:['node-base', 'event-custom-complex']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('event-focus', function(Y) { /** * Adds bubbling and delegation support to DOM events focus and blur. * * @module event * @submodule event-focus */ var Event = Y.Event, YLang = Y.Lang, isString = YLang.isString, arrayIndex = Y.Array.indexOf, useActivate = YLang.isFunction( Y.DOM.create('

').onbeforeactivate); function define(type, proxy, directEvent) { var nodeDataKey = '_' + type + 'Notifiers'; Y.Event.define(type, { _attach: function (el, notifier, delegate) { if (Y.DOM.isWindow(el)) { return Event._attach([type, function (e) { notifier.fire(e); }, el]); } else { return Event._attach( [proxy, this._proxy, el, this, notifier, delegate], { capture: true }); } }, _proxy: function (e, notifier, delegate) { var target = e.target, currentTarget = e.currentTarget, notifiers = target.getData(nodeDataKey), yuid = Y.stamp(currentTarget._node), defer = (useActivate || target !== currentTarget), directSub; notifier.currentTarget = (delegate) ? target : currentTarget; notifier.container = (delegate) ? currentTarget : null; // Maintain a list to handle subscriptions from nested // containers div#a>div#b>input #a.on(focus..) #b.on(focus..), // use one focus or blur subscription that fires notifiers from // #b then #a to emulate bubble sequence. if (!notifiers) { notifiers = {}; target.setData(nodeDataKey, notifiers); // only subscribe to the element's focus if the target is // not the current target ( if (defer) { directSub = Event._attach( [directEvent, this._notify, target._node]).sub; directSub.once = true; } } else { // In old IE, defer is always true. In capture-phase browsers, // The delegate subscriptions will be encountered first, which // will establish the notifiers data and direct subscription // on the node. If there is also a direct subscription to the // node's focus/blur, it should not call _notify because the // direct subscription from the delegate sub(s) exists, which // will call _notify. So this avoids _notify being called // twice, unnecessarily. defer = true; } if (!notifiers[yuid]) { notifiers[yuid] = []; } notifiers[yuid].push(notifier); if (!defer) { this._notify(e); } }, _notify: function (e, container) { var currentTarget = e.currentTarget, notifierData = currentTarget.getData(nodeDataKey), axisNodes = currentTarget.ancestors(), doc = currentTarget.get('ownerDocument'), delegates = [], // Used to escape loops when there are no more // notifiers to consider count = notifierData ? Y.Object.keys(notifierData).length : 0, target, notifiers, notifier, yuid, match, tmp, i, len, sub, ret; // clear the notifications list (mainly for delegation) currentTarget.clearData(nodeDataKey); // Order the delegate subs by their placement in the parent axis axisNodes.push(currentTarget); // document.get('ownerDocument') returns null // which we'll use to prevent having duplicate Nodes in the list if (doc) { axisNodes.unshift(doc); } // ancestors() returns the Nodes from top to bottom axisNodes._nodes.reverse(); // Store the count for step 2 tmp = count; axisNodes.some(function (node) { var yuid = Y.stamp(node), notifiers = notifierData[yuid], i, len; if (notifiers) { count--; for (i = 0, len = notifiers.length; i < len; ++i) { if (notifiers[i].handle.sub.filter) { delegates.push(notifiers[i]); } } } return !count; }); count = tmp; // Walk up the parent axis, notifying direct subscriptions and // testing delegate filters. while (count && (target = axisNodes.shift())) { yuid = Y.stamp(target); notifiers = notifierData[yuid]; if (notifiers) { for (i = 0, len = notifiers.length; i < len; ++i) { notifier = notifiers[i]; sub = notifier.handle.sub; match = true; e.currentTarget = target; if (sub.filter) { match = sub.filter.apply(target, [target, e].concat(sub.args || [])); // No longer necessary to test against this // delegate subscription for the nodes along // the parent axis. delegates.splice( arrayIndex(delegates, notifier), 1); } if (match) { // undefined for direct subs e.container = notifier.container; ret = notifier.fire(e); } if (ret === false || e.stopped === 2) { break; } } delete notifiers[yuid]; count--; } if (e.stopped !== 2) { // delegates come after subs targeting this specific node // because they would not normally report until they'd // bubbled to the container node. for (i = 0, len = delegates.length; i < len; ++i) { notifier = delegates[i]; sub = notifier.handle.sub; if (sub.filter.apply(target, [target, e].concat(sub.args || []))) { e.container = notifier.container; e.currentTarget = target; ret = notifier.fire(e); } if (ret === false || e.stopped === 2) { break; } } } if (e.stopped) { break; } } }, on: function (node, sub, notifier) { sub.handle = this._attach(node._node, notifier); }, detach: function (node, sub) { sub.handle.detach(); }, delegate: function (node, sub, notifier, filter) { if (isString(filter)) { sub.filter = function (target) { return Y.Selector.test(target._node, filter, node === target ? null : node._node); }; } sub.handle = this._attach(node._node, notifier, true); }, detachDelegate: function (node, sub) { sub.handle.detach(); } }, true); } // For IE, we need to defer to focusin rather than focus because // `el.focus(); doSomething();` executes el.onbeforeactivate, el.onactivate, // el.onfocusin, doSomething, then el.onfocus. All others support capture // phase focus, which executes before doSomething. To guarantee consistent // behavior for this use case, IE's direct subscriptions are made against // focusin so subscribers will be notified before js following el.focus() is // executed. if (useActivate) { // name capture phase direct subscription define("focus", "beforeactivate", "focusin"); define("blur", "beforedeactivate", "focusout"); } else { define("focus", "focus", "focus"); define("blur", "blur", "blur"); } }, '3.5.1' ,{requires:['event-synthetic']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('widget-base', function(Y) { /** * Provides the base Widget class, with HTML Parser support * * @module widget * @main widget */ /** * Provides the base Widget class * * @module widget * @submodule widget-base */ var L = Y.Lang, Node = Y.Node, ClassNameManager = Y.ClassNameManager, _getClassName = ClassNameManager.getClassName, _getWidgetClassName, _toInitialCap = Y.cached(function(str) { return str.substring(0, 1).toUpperCase() + str.substring(1); }), // K-Weight, IE GC optimizations CONTENT = "content", VISIBLE = "visible", HIDDEN = "hidden", DISABLED = "disabled", FOCUSED = "focused", WIDTH = "width", HEIGHT = "height", BOUNDING_BOX = "boundingBox", CONTENT_BOX = "contentBox", PARENT_NODE = "parentNode", OWNER_DOCUMENT = "ownerDocument", AUTO = "auto", SRC_NODE = "srcNode", BODY = "body", TAB_INDEX = "tabIndex", ID = "id", RENDER = "render", RENDERED = "rendered", DESTROYED = "destroyed", STRINGS = "strings", DIV = "

", CHANGE = "Change", LOADING = "loading", _UISET = "_uiSet", EMPTY_STR = "", EMPTY_FN = function() {}, TRUE = true, FALSE = false, UI, ATTRS = {}, UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED, TAB_INDEX], WEBKIT = Y.UA.webkit, // Widget nodeid-to-instance map. _instances = {}; /** * A base class for widgets, providing: * * * @param config {Object} Object literal specifying widget configuration properties. * * @class Widget * @constructor * @extends Base */ function Widget(config) { // kweight var widget = this, parentNode, render, constructor = widget.constructor; widget._strs = {}; widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase()); // We need a config for HTML_PARSER to work. config = config || {}; Widget.superclass.constructor.call(widget, config); render = widget.get(RENDER); if (render) { // Render could be a node or boolean if (render !== TRUE) { parentNode = render; } widget.render(parentNode); } } /** * Static property provides a string to identify the class. *

* Currently used to apply class identifiers to the bounding box * and to classify events fired by the widget. *

* * @property NAME * @type String * @static */ Widget.NAME = "widget"; /** * Constant used to identify state changes originating from * the DOM (as opposed to the JavaScript model). * * @property UI_SRC * @type String * @static * @final */ UI = Widget.UI_SRC = "ui"; /** * Static property used to define the default attribute * configuration for the Widget. * * @property ATTRS * @type Object * @static */ Widget.ATTRS = ATTRS; // Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd /** * @attribute id * @writeOnce * @default Generated using guid() * @type String */ ATTRS[ID] = { valueFn: "_guid", writeOnce: TRUE }; /** * Flag indicating whether or not this Widget * has been through the render lifecycle phase. * * @attribute rendered * @readOnly * @default false * @type boolean */ ATTRS[RENDERED] = { value:FALSE, readOnly: TRUE }; /** * @attribute boundingBox * @description The outermost DOM node for the Widget, used for sizing and positioning * of a Widget as well as a containing element for any decorator elements used * for skinning. * @type String | Node * @writeOnce */ ATTRS[BOUNDING_BOX] = { value:null, setter: "_setBB", writeOnce: TRUE }; /** * @attribute contentBox * @description A DOM node that is a direct descendant of a Widget's bounding box that * houses its content. * @type String | Node * @writeOnce */ ATTRS[CONTENT_BOX] = { valueFn:"_defaultCB", setter: "_setCB", writeOnce: TRUE }; /** * @attribute tabIndex * @description Number (between -32767 to 32767) indicating the widget's * position in the default tab flow. The value is used to set the * "tabIndex" attribute on the widget's bounding box. Negative values allow * the widget to receive DOM focus programmatically (by calling the focus * method), while being removed from the default tab flow. A value of * null removes the "tabIndex" attribute from the widget's bounding box. * @type Number * @default null */ ATTRS[TAB_INDEX] = { value: null, validator: "_validTabIndex" }; /** * @attribute focused * @description Boolean indicating if the Widget, or one of its descendants, * has focus. * @readOnly * @default false * @type boolean */ ATTRS[FOCUSED] = { value: FALSE, readOnly:TRUE }; /** * @attribute disabled * @description Boolean indicating if the Widget should be disabled. The disabled implementation * is left to the specific classes extending widget. * @default false * @type boolean */ ATTRS[DISABLED] = { value: FALSE }; /** * @attribute visible * @description Boolean indicating weather or not the Widget is visible. * @default TRUE * @type boolean */ ATTRS[VISIBLE] = { value: TRUE }; /** * @attribute height * @description String with units, or number, representing the height of the Widget. If a number is provided, * the default unit, defined by the Widgets DEF_UNIT, property is used. * @default EMPTY_STR * @type {String | Number} */ ATTRS[HEIGHT] = { value: EMPTY_STR }; /** * @attribute width * @description String with units, or number, representing the width of the Widget. If a number is provided, * the default unit, defined by the Widgets DEF_UNIT, property is used. * @default EMPTY_STR * @type {String | Number} */ ATTRS[WIDTH] = { value: EMPTY_STR }; /** * @attribute strings * @description Collection of strings used to label elements of the Widget's UI. * @default null * @type Object */ ATTRS[STRINGS] = { value: {}, setter: "_strSetter", getter: "_strGetter" }; /** * Whether or not to render the widget automatically after init, and optionally, to which parent node. * * @attribute render * @type boolean | Node * @writeOnce */ ATTRS[RENDER] = { value:FALSE, writeOnce:TRUE }; /** * The css prefix which the static Widget.getClassName method should use when constructing class names * * @property CSS_PREFIX * @type String * @default Widget.NAME.toLowerCase() * @private * @static */ Widget.CSS_PREFIX = _getClassName(Widget.NAME.toLowerCase()); /** * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined * by the Y.config.classNamePrefix attribute used by ClassNameManager and * Widget.NAME.toLowerCase() (e.g. "yui-widget-xxxxx-yyyyy", based on default values for * the prefix and widget class name). *

* The instance based version of this method can be used to generate standard prefixed classnames, * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you * need to use a constant class name across different types instances. *

* @method getClassName * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name */ Widget.getClassName = function() { // arguments needs to be array'fied to concat return _getClassName.apply(ClassNameManager, [Widget.CSS_PREFIX].concat(Y.Array(arguments), true)); }; _getWidgetClassName = Widget.getClassName; /** * Returns the widget instance whose bounding box contains, or is, the given node. *

* In the case of nested widgets, the nearest bounding box ancestor is used to * return the widget instance. *

* @method getByNode * @static * @param node {Node | String} The node for which to return a Widget instance. If a selector * string is passed in, which selects more than one node, the first node found is used. * @return {Widget} Widget instance, or null if not found. */ Widget.getByNode = function(node) { var widget, nodeid, widgetMarker = _getWidgetClassName(); node = Node.one(node); if (node) { node = node.ancestor("." + widgetMarker, true); if (node) { nodeid = node.get(ID); widget = _instances[nodeid]; } } return widget || null; }; Y.extend(Widget, Y.Base, { /** * Returns a class name prefixed with the the value of the * YUI.config.classNamePrefix attribute + the instances NAME property. * Uses YUI.config.classNameDelimiter attribute to delimit the provided strings. * e.g. * *
     *    // returns "yui-slider-foo-bar", for a slider instance
     *    var scn = slider.getClassName('foo','bar');
     *
     *    // returns "yui-overlay-foo-bar", for an overlay instance
     *    var ocn = overlay.getClassName('foo','bar');
     * 
*
* * @method getClassName * @param {String}+ One or more classname bits to be joined and prefixed */ getClassName: function () { return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true)); }, /** * Initializer lifecycle implementation for the Widget class. Registers the * widget instance, and runs through the Widget's HTML_PARSER definition. * * @method initializer * @protected * @param config {Object} Configuration object literal for the widget */ initializer: function(config) { var bb = this.get(BOUNDING_BOX); if (bb instanceof Node) { this._mapInstance(bb.get(ID)); } /** * Notification event, which widget implementations can fire, when * they change the content of the widget. This event has no default * behavior and cannot be prevented, so the "on" or "after" * moments are effectively equivalent (with on listeners being invoked before * after listeners). * * @event widget:contentUpdate * @preventable false * @param {EventFacade} e The Event Facade */ if (this._applyParser) { this._applyParser(config); } }, /** * Utility method used to add an entry to the boundingBox id to instance map. * * This method can be used to populate the instance with lazily created boundingBox Node references. * * @method _mapInstance * @param {String} The boundingBox id * @protected */ _mapInstance : function(id) { if (!(_instances[id])) { _instances[id] = this; } }, /** * Destructor lifecycle implementation for the Widget class. Purges events attached * to the bounding box and content box, removes them from the DOM and removes * the Widget from the list of registered widgets. * * @method destructor * @protected */ destructor: function() { var boundingBox = this.get(BOUNDING_BOX), bbid; if (boundingBox instanceof Node) { bbid = boundingBox.get(ID); if (bbid in _instances) { delete _instances[bbid]; } this._destroyBox(); } }, /** *

* Destroy lifecycle method. Fires the destroy * event, prior to invoking destructors for the * class hierarchy. * * Overrides Base's implementation, to support arguments to destroy *

*

* Subscribers to the destroy * event can invoke preventDefault on the event object, to prevent destruction * from proceeding. *

* @method destroy * @param destroyAllNodes {Boolean} If true, all nodes contained within the Widget are removed and destroyed. Defaults to false due to potentially high run-time cost. * @return {Widget} A reference to this object * @chainable */ destroy: function(destroyAllNodes) { this._destroyAllNodes = destroyAllNodes; return Widget.superclass.destroy.apply(this); }, /** * Removes and destroys the widgets rendered boundingBox, contentBox, * and detaches bound UI events. * * @method _destroyBox * @protected */ _destroyBox : function() { var boundingBox = this.get(BOUNDING_BOX), contentBox = this.get(CONTENT_BOX), deep = this._destroyAllNodes, same; same = boundingBox && boundingBox.compareTo(contentBox); if (this.UI_EVENTS) { this._destroyUIEvents(); } this._unbindUI(boundingBox); if (deep) { // Removes and destroys all child nodes. boundingBox.empty(); boundingBox.remove(TRUE); } else { if (contentBox) { contentBox.remove(TRUE); } if (!same) { boundingBox.remove(TRUE); } } }, /** * Establishes the initial DOM for the widget. Invoking this * method will lead to the creating of all DOM elements for * the widget (or the manipulation of existing DOM elements * for the progressive enhancement use case). *

* This method should only be invoked once for an initialized * widget. *

*

* It delegates to the widget specific renderer method to do * the actual work. *

* * @method render * @chainable * @final * @param parentNode {Object | String} Optional. The Node under which the * Widget is to be rendered. This can be a Node instance or a CSS selector string. *

* If the selector string returns more than one Node, the first node will be used * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox * are not currently in the document. If it's not provided, the Widget will be rendered * to the body of the current document in this case. *

*/ render: function(parentNode) { if (!this.get(DESTROYED) && !this.get(RENDERED)) { /** * Lifecycle event for the render phase, fired prior to rendering the UI * for the widget (prior to invoking the widget's renderer method). *

* Subscribers to the "on" moment of this event, will be notified * before the widget is rendered. *

*

* Subscribers to the "after" moment of this event, will be notified * after rendering is complete. *

* * @event widget:render * @preventable _defRenderFn * @param {EventFacade} e The Event Facade */ this.publish(RENDER, { queuable:FALSE, fireOnce:TRUE, defaultTargetOnly:TRUE, defaultFn: this._defRenderFn }); this.fire(RENDER, {parentNode: (parentNode) ? Node.one(parentNode) : null}); } return this; }, /** * Default render handler * * @method _defRenderFn * @protected * @param {EventFacade} e The Event object * @param {Node} parentNode The parent node to render to, if passed in to the render method */ _defRenderFn : function(e) { this._parentNode = e.parentNode; this.renderer(); this._set(RENDERED, TRUE); this._removeLoadingClassNames(); }, /** * Creates DOM (or manipulates DOM for progressive enhancement) * This method is invoked by render() and is not chained * automatically for the class hierarchy (unlike initializer, destructor) * so it should be chained manually for subclasses if required. * * @method renderer * @protected */ renderer: function() { // kweight var widget = this; widget._renderUI(); widget.renderUI(); widget._bindUI(); widget.bindUI(); widget._syncUI(); widget.syncUI(); }, /** * Configures/Sets up listeners to bind Widget State to UI/DOM * * This method is not called by framework and is not chained * automatically for the class hierarchy. * * @method bindUI * @protected */ bindUI: EMPTY_FN, /** * Adds nodes to the DOM * * This method is not called by framework and is not chained * automatically for the class hierarchy. * * @method renderUI * @protected */ renderUI: EMPTY_FN, /** * Refreshes the rendered UI, based on Widget State * * This method is not called by framework and is not chained * automatically for the class hierarchy. * * @method syncUI * @protected * */ syncUI: EMPTY_FN, /** * @method hide * @description Hides the Widget by setting the "visible" attribute to "false". * @chainable */ hide: function() { return this.set(VISIBLE, FALSE); }, /** * @method show * @description Shows the Widget by setting the "visible" attribute to "true". * @chainable */ show: function() { return this.set(VISIBLE, TRUE); }, /** * @method focus * @description Causes the Widget to receive the focus by setting the "focused" * attribute to "true". * @chainable */ focus: function () { return this._set(FOCUSED, TRUE); }, /** * @method blur * @description Causes the Widget to lose focus by setting the "focused" attribute * to "false" * @chainable */ blur: function () { return this._set(FOCUSED, FALSE); }, /** * @method enable * @description Set the Widget's "disabled" attribute to "false". * @chainable */ enable: function() { return this.set(DISABLED, FALSE); }, /** * @method disable * @description Set the Widget's "disabled" attribute to "true". * @chainable */ disable: function() { return this.set(DISABLED, TRUE); }, /** * @method _uiSizeCB * @protected * @param {boolean} expand */ _uiSizeCB : function(expand) { this.get(CONTENT_BOX).toggleClass(_getWidgetClassName(CONTENT, "expanded"), expand); }, /** * Helper method to collect the boundingBox and contentBox and append to the provided parentNode, if not * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered * to the current document's body. * * @method _renderBox * @private * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and * the contentBox are not currently in the document, the widget will be rendered to the current document's body. */ _renderBox: function(parentNode) { // TODO: Performance Optimization [ More effective algo to reduce Node refs, compares, replaces? ] var widget = this, // kweight contentBox = widget.get(CONTENT_BOX), boundingBox = widget.get(BOUNDING_BOX), srcNode = widget.get(SRC_NODE), defParentNode = widget.DEF_PARENT_NODE, doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT); // If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents) if (srcNode && !srcNode.compareTo(contentBox) && !contentBox.inDoc(doc)) { srcNode.replace(contentBox); } if (!boundingBox.compareTo(contentBox.get(PARENT_NODE)) && !boundingBox.compareTo(contentBox)) { // If contentBox box is already in the document, have boundingBox box take it's place if (contentBox.inDoc(doc)) { contentBox.replace(boundingBox); } boundingBox.appendChild(contentBox); } parentNode = parentNode || (defParentNode && Node.one(defParentNode)); if (parentNode) { parentNode.appendChild(boundingBox); } else if (!boundingBox.inDoc(doc)) { Node.one(BODY).insert(boundingBox, 0); } }, /** * Setter for the boundingBox attribute * * @method _setBB * @private * @param Node/String * @return Node */ _setBB: function(node) { return this._setBox(this.get(ID), node, this.BOUNDING_TEMPLATE); }, /** * Setter for the contentBox attribute * * @method _setCB * @private * @param {Node|String} node * @return Node */ _setCB: function(node) { return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE); }, /** * Returns the default value for the contentBox attribute. * * For the Widget class, this will be the srcNode if provided, otherwise null (resulting in * a new contentBox node instance being created) * * @method _defaultCB * @protected */ _defaultCB : function(node) { return this.get(SRC_NODE) || null; }, /** * Helper method to set the bounding/content box, or create it from * the provided template if not found. * * @method _setBox * @private * * @param {String} id The node's id attribute * @param {Node|String} node The node reference * @param {String} template HTML string template for the node * @return {Node} The node */ _setBox : function(id, node, template) { node = Node.one(node) || Node.create(template); if (!node.get(ID)) { node.set(ID, id || Y.guid()); } return node; }, /** * Initializes the UI state for the Widget's bounding/content boxes. * * @method _renderUI * @protected */ _renderUI: function() { this._renderBoxClassNames(); this._renderBox(this._parentNode); }, /** * Applies standard class names to the boundingBox and contentBox * * @method _renderBoxClassNames * @protected */ _renderBoxClassNames : function() { var classes = this._getClasses(), cl, boundingBox = this.get(BOUNDING_BOX), i; boundingBox.addClass(_getWidgetClassName()); // Start from Widget Sub Class for (i = classes.length-3; i >= 0; i--) { cl = classes[i]; boundingBox.addClass(cl.CSS_PREFIX || _getClassName(cl.NAME.toLowerCase())); } // Use instance based name for content box this.get(CONTENT_BOX).addClass(this.getClassName(CONTENT)); }, /** * Removes class names representative of the widget's loading state from * the boundingBox. * * @method _removeLoadingClassNames * @protected */ _removeLoadingClassNames: function () { var boundingBox = this.get(BOUNDING_BOX), contentBox = this.get(CONTENT_BOX), instClass = this.getClassName(LOADING), widgetClass = _getWidgetClassName(LOADING); boundingBox.removeClass(widgetClass) .removeClass(instClass); contentBox.removeClass(widgetClass) .removeClass(instClass); }, /** * Sets up DOM and CustomEvent listeners for the widget. * * @method _bindUI * @protected */ _bindUI: function() { this._bindAttrUI(this._UI_ATTRS.BIND); this._bindDOM(); }, /** * @method _unbindUI * @protected */ _unbindUI : function(boundingBox) { this._unbindDOM(boundingBox); }, /** * Sets up DOM listeners, on elements rendered by the widget. * * @method _bindDOM * @protected */ _bindDOM : function() { var oDocument = this.get(BOUNDING_BOX).get(OWNER_DOCUMENT), focusHandle = Widget._hDocFocus; // Shared listener across all Widgets. if (!focusHandle) { focusHandle = Widget._hDocFocus = oDocument.on("focus", this._onDocFocus, this); focusHandle.listeners = { count: 0 }; } focusHandle.listeners[Y.stamp(this, true)] = true; focusHandle.listeners.count++; // Fix for Webkit: // Document doesn't receive focus in Webkit when the user mouses // down on it, so the "focused" attribute won't get set to the // correct value. Keeping this instance based for now, potential better performance. // Otherwise we'll end up looking up widgets from the DOM on every mousedown. if (WEBKIT){ this._hDocMouseDown = oDocument.on("mousedown", this._onDocMouseDown, this); } }, /** * @method _unbindDOM * @protected */ _unbindDOM : function(boundingBox) { var focusHandle = Widget._hDocFocus, yuid = Y.stamp(this, true), focusListeners, mouseHandle = this._hDocMouseDown; if (focusHandle) { focusListeners = focusHandle.listeners; if (focusListeners[yuid]) { delete focusListeners[yuid]; focusListeners.count--; } if (focusListeners.count === 0) { focusHandle.detach(); Widget._hDocFocus = null; } } if (WEBKIT && mouseHandle) { mouseHandle.detach(); } }, /** * Updates the widget UI to reflect the attribute state. * * @method _syncUI * @protected */ _syncUI: function() { this._syncAttrUI(this._UI_ATTRS.SYNC); }, /** * Sets the height on the widget's bounding box element * * @method _uiSetHeight * @protected * @param {String | Number} val */ _uiSetHeight: function(val) { this._uiSetDim(HEIGHT, val); this._uiSizeCB((val !== EMPTY_STR && val !== AUTO)); }, /** * Sets the width on the widget's bounding box element * * @method _uiSetWidth * @protected * @param {String | Number} val */ _uiSetWidth: function(val) { this._uiSetDim(WIDTH, val); }, /** * @method _uiSetDim * @private * @param {String} dim The dimension - "width" or "height" * @param {Number | String} val The value to set */ _uiSetDim: function(dimension, val) { this.get(BOUNDING_BOX).setStyle(dimension, L.isNumber(val) ? val + this.DEF_UNIT : val); }, /** * Sets the visible state for the UI * * @method _uiSetVisible * @protected * @param {boolean} val */ _uiSetVisible: function(val) { this.get(BOUNDING_BOX).toggleClass(this.getClassName(HIDDEN), !val); }, /** * Sets the disabled state for the UI * * @method _uiSetDisabled * @protected * @param {boolean} val */ _uiSetDisabled: function(val) { this.get(BOUNDING_BOX).toggleClass(this.getClassName(DISABLED), val); }, /** * Sets the focused state for the UI * * @method _uiSetFocused * @protected * @param {boolean} val * @param {string} src String representing the source that triggered an update to * the UI. */ _uiSetFocused: function(val, src) { var boundingBox = this.get(BOUNDING_BOX); boundingBox.toggleClass(this.getClassName(FOCUSED), val); if (src !== UI) { if (val) { boundingBox.focus(); } else { boundingBox.blur(); } } }, /** * Set the tabIndex on the widget's rendered UI * * @method _uiSetTabIndex * @protected * @param Number */ _uiSetTabIndex: function(index) { var boundingBox = this.get(BOUNDING_BOX); if (L.isNumber(index)) { boundingBox.set(TAB_INDEX, index); } else { boundingBox.removeAttribute(TAB_INDEX); } }, /** * @method _onDocMouseDown * @description "mousedown" event handler for the owner document of the * widget's bounding box. * @protected * @param {EventFacade} evt The event facade for the DOM focus event */ _onDocMouseDown: function (evt) { if (this._domFocus) { this._onDocFocus(evt); } }, /** * DOM focus event handler, used to sync the state of the Widget with the DOM * * @method _onDocFocus * @protected * @param {EventFacade} evt The event facade for the DOM focus event */ _onDocFocus: function (evt) { var widget = Widget.getByNode(evt.target), activeWidget = Widget._active; if (activeWidget && (activeWidget !== widget)) { activeWidget._domFocus = false; activeWidget._set(FOCUSED, false, {src:UI}); Widget._active = null; } if (widget) { widget._domFocus = true; widget._set(FOCUSED, true, {src:UI}); Widget._active = widget; } }, /** * Generic toString implementation for all widgets. * * @method toString * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ] */ toString: function() { // Using deprecated name prop for kweight squeeze. return this.name + "[" + this.get(ID) + "]"; }, /** * Default unit to use for dimension values * * @property DEF_UNIT * @type String */ DEF_UNIT : "px", /** * Default node to render the bounding box to. If not set, * will default to the current document body. * * @property DEF_PARENT_NODE * @type String | Node */ DEF_PARENT_NODE : null, /** * Property defining the markup template for content box. If your Widget doesn't * need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null, * and contentBox and boundingBox will both point to the same Node. * * @property CONTENT_TEMPLATE * @type String */ CONTENT_TEMPLATE : DIV, /** * Property defining the markup template for bounding box. * * @property BOUNDING_TEMPLATE * @type String */ BOUNDING_TEMPLATE : DIV, /** * @method _guid * @protected */ _guid : function() { return Y.guid(); }, /** * @method _validTabIndex * @protected * @param {Number} tabIndex */ _validTabIndex : function (tabIndex) { return (L.isNumber(tabIndex) || L.isNull(tabIndex)); }, /** * Binds after listeners for the list of attributes provided * * @method _bindAttrUI * @private * @param {Array} attrs */ _bindAttrUI : function(attrs) { var i, l = attrs.length; for (i = 0; i < l; i++) { this.after(attrs[i] + CHANGE, this._setAttrUI); } }, /** * Invokes the _uiSet=ATTR NAME> method for the list of attributes provided * * @method _syncAttrUI * @private * @param {Array} attrs */ _syncAttrUI : function(attrs) { var i, l = attrs.length, attr; for (i = 0; i < l; i++) { attr = attrs[i]; this[_UISET + _toInitialCap(attr)](this.get(attr)); } }, /** * @method _setAttrUI * @private * @param {EventFacade} e */ _setAttrUI : function(e) { if (e.target === this) { this[_UISET + _toInitialCap(e.attrName)](e.newVal, e.src); } }, /** * The default setter for the strings attribute. Merges partial sets * into the full string set, to allow users to partial sets of strings * * @method _strSetter * @protected * @param {Object} strings * @return {String} The full set of strings to set */ _strSetter : function(strings) { return Y.merge(this.get(STRINGS), strings); }, /** * Helper method to get a specific string value * * @deprecated Used by deprecated WidgetLocale implementations. * @method getString * @param {String} key * @return {String} The string */ getString : function(key) { return this.get(STRINGS)[key]; }, /** * Helper method to get the complete set of strings for the widget * * @deprecated Used by deprecated WidgetLocale implementations. * @method getStrings * @param {String} key * @return {String} The strings */ getStrings : function() { return this.get(STRINGS); }, /** * The lists of UI attributes to bind and sync for widget's _bindUI and _syncUI implementations * * @property _UI_ATTRS * @type Object * @private */ _UI_ATTRS : { BIND: UI_ATTRS, SYNC: UI_ATTRS } }); Y.Widget = Widget; }, '3.5.1' ,{requires:['attribute', 'event-focus', 'base-base', 'base-pluginhost', 'node-base', 'node-style', 'classnamemanager'], skinnable:true}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('widget-htmlparser', function(Y) { /** * Adds HTML Parser support to the base Widget class * * @module widget * @submodule widget-htmlparser * @for Widget */ var Widget = Y.Widget, Node = Y.Node, Lang = Y.Lang, SRC_NODE = "srcNode", CONTENT_BOX = "contentBox"; /** * Object hash, defining how attribute values are to be parsed from * markup contained in the widget's content box. e.g.: *
 *   {
 *       // Set single Node references using selector syntax 
 *       // (selector is run through node.one)
 *       titleNode: "span.yui-title",
 *       // Set NodeList references using selector syntax 
 *       // (array indicates selector is to be run through node.all)
 *       listNodes: ["li.yui-item"],
 *       // Set other attribute types, using a parse function. 
 *       // Context is set to the widget instance.
 *       label: function(contentBox) {
 *           return contentBox.one("span.title").get("innerHTML");
 *       }
 *   }
 * 
* * @property HTML_PARSER * @type Object * @static */ Widget.HTML_PARSER = {}; /** * The build configuration for the Widget class. *

* Defines the static fields which need to be aggregated, * when this class is used as the main class passed to * the Base.build method. *

* @property _buildCfg * @type Object * @static * @final * @private */ Widget._buildCfg = { aggregates : ["HTML_PARSER"] }; /** * The DOM node to parse for configuration values, passed to the Widget's HTML_PARSER definition * * @attribute srcNode * @type String | Node * @writeOnce */ Widget.ATTRS[SRC_NODE] = { value: null, setter: Node.one, getter: "_getSrcNode", writeOnce: true }; Y.mix(Widget.prototype, { /** * @method _getSrcNode * @protected * @return {Node} The Node to apply HTML_PARSER to */ _getSrcNode : function(val) { return val || this.get(CONTENT_BOX); }, /** * @method _applyParsedConfig * @protected * @return {Object} The merged configuration literal */ _applyParsedConfig : function(node, cfg, parsedCfg) { return (parsedCfg) ? Y.mix(cfg, parsedCfg, false) : cfg; }, /** * Utilitity method used to apply the HTML_PARSER configuration for the * instance, to retrieve config data values. * * @method _applyParser * @protected * @param config {Object} User configuration object (will be populated with values from Node) */ _applyParser : function(config) { var widget = this, srcNode = widget.get(SRC_NODE), schema = widget._getHtmlParser(), parsedConfig, val; if (schema && srcNode) { Y.Object.each(schema, function(v, k, o) { val = null; if (Lang.isFunction(v)) { val = v.call(widget, srcNode); } else { if (Lang.isArray(v)) { val = srcNode.all(v[0]); if (val.isEmpty()) { val = null; } } else { val = srcNode.one(v); } } if (val !== null && val !== undefined) { parsedConfig = parsedConfig || {}; parsedConfig[k] = val; } }); } config = widget._applyParsedConfig(srcNode, config, parsedConfig); }, /** * Gets the HTML_PARSER definition for this instance, by merging HTML_PARSER * definitions across the class hierarchy. * * @private * @method _getHtmlParser * @return {Object} HTML_PARSER definition for this instance */ _getHtmlParser : function() { // Removed caching for kweight. This is a private method // and only called once so don't need to cache HTML_PARSER var classes = this._getClasses(), parser = {}, i, p; for (i = classes.length - 1; i >= 0; i--) { p = classes[i].HTML_PARSER; if (p) { Y.mix(parser, p, true); } } return parser; } }); }, '3.5.1' ,{requires:['widget-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('widget-skin', function(Y) { /** * Provides skin related utlility methods. * * @module widget * @submodule widget-skin */ var BOUNDING_BOX = "boundingBox", CONTENT_BOX = "contentBox", SKIN = "skin", _getClassName = Y.ClassNameManager.getClassName; /** * Returns the name of the skin that's currently applied to the widget. * This is only really useful after the widget's DOM structure is in the * document, either by render or by progressive enhancement. Searches up * the Widget's ancestor axis for a class yui3-skin-(name), and returns the * (name) portion. Otherwise, returns null. * * @method getSkinName * @for Widget * @return {String} the name of the skin, or null (yui3-skin-sam => sam) */ Y.Widget.prototype.getSkinName = function () { var root = this.get( CONTENT_BOX ) || this.get( BOUNDING_BOX ), search = new RegExp( '\\b' + _getClassName( SKIN ) + '-(\\S+)' ), match; if ( root ) { root.ancestor( function ( node ) { match = node.get( 'className' ).match( search ); return match; } ); } return ( match ) ? match[1] : null; }; }, '3.5.1' ,{requires:['widget-base']}); /* YUI 3.5.1 (build 22) Copyright 2012 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('widget-uievents', function(Y) { /** * Support for Widget UI Events (Custom Events fired by the widget, which wrap the underlying DOM events - e.g. widget:click, widget:mousedown) * * @module widget * @submodule widget-uievents */ var BOUNDING_BOX = "boundingBox", Widget = Y.Widget, RENDER = "render", L = Y.Lang, EVENT_PREFIX_DELIMITER = ":", // Map of Node instances serving as a delegation containers for a specific // event type to Widget instances using that delegation container. _uievts = Y.Widget._uievts = Y.Widget._uievts || {}; Y.mix(Widget.prototype, { /** * Destructor logic for UI event infrastructure, * invoked during Widget destruction. * * @method _destroyUIEvents * @for Widget * @private */ _destroyUIEvents: function() { var widgetGuid = Y.stamp(this, true); Y.each(_uievts, function (info, key) { if (info.instances[widgetGuid]) { // Unregister this Widget instance as needing this delegated // event listener. delete info.instances[widgetGuid]; // There are no more Widget instances using this delegated // event listener, so detach it. if (Y.Object.isEmpty(info.instances)) { info.handle.detach(); if (_uievts[key]) { delete _uievts[key]; } } } }); }, /** * Map of DOM events that should be fired as Custom Events by the * Widget instance. * * @property UI_EVENTS * @for Widget * @type Object */ UI_EVENTS: Y.Node.DOM_EVENTS, /** * Returns the node on which to bind delegate listeners. * * @method _getUIEventNode * @for Widget * @protected */ _getUIEventNode: function () { return this.get(BOUNDING_BOX); }, /** * Binds a delegated DOM event listener of the specified type to the * Widget's outtermost DOM element to facilitate the firing of a Custom * Event of the same type for the Widget instance. * * @method _createUIEvent * @for Widget * @param type {String} String representing the name of the event * @private */ _createUIEvent: function (type) { var uiEvtNode = this._getUIEventNode(), key = (Y.stamp(uiEvtNode) + type), info = _uievts[key], handle; // For each Node instance: Ensure that there is only one delegated // event listener used to fire Widget UI events. if (!info) { handle = uiEvtNode.delegate(type, function (evt) { var widget = Widget.getByNode(this); // Widget could be null if node instance belongs to // another Y instance. if (widget) { if (widget._filterUIEvent(evt)) { widget.fire(evt.type, { domEvent: evt }); } } }, "." + Y.Widget.getClassName()); _uievts[key] = info = { instances: {}, handle: handle }; } // Register this Widget as using this Node as a delegation container. info.instances[Y.stamp(this)] = 1; }, /** * This method is used to determine if we should fire * the UI Event or not. The default implementation makes sure * that for nested delegates (nested unrelated widgets), we don't * fire the UI event listener more than once at each level. * *

For example, without the additional filter, if you have nested * widgets, each widget will have a delegate listener. If you * click on the inner widget, the inner delegate listener's * filter will match once, but the outer will match twice * (based on delegate's design) - once for the inner widget, * and once for the outer.

* * @method _filterUIEvent * @for Widget * @param {DOMEventFacade} evt * @return {boolean} true if it's OK to fire the custom UI event, false if not. * @private * */ _filterUIEvent: function(evt) { // Either it's hitting this widget's delegate container (and not some other widget's), // or the container it's hitting is handling this widget's ui events. return (evt.currentTarget.compareTo(evt.container) || evt.container.compareTo(this._getUIEventNode())); }, /** * Determines if the specified event is a UI event. * * @private * @method _isUIEvent * @for Widget * @param type {String} String representing the name of the event * @return {String} Event Returns the name of the UI Event, otherwise * undefined. */ _getUIEvent: function (type) { if (L.isString(type)) { var sType = this.parseType(type)[1], iDelim, returnVal; if (sType) { // TODO: Get delimiter from ET, or have ET support this. iDelim = sType.indexOf(EVENT_PREFIX_DELIMITER); if (iDelim > -1) { sType = sType.substring(iDelim + EVENT_PREFIX_DELIMITER.length); } if (this.UI_EVENTS[sType]) { returnVal = sType; } } return returnVal; } }, /** * Sets up infrastructure required to fire a UI event. * * @private * @method _initUIEvent * @for Widget * @param type {String} String representing the name of the event * @return {String} */ _initUIEvent: function (type) { var sType = this._getUIEvent(type), queue = this._uiEvtsInitQueue || {}; if (sType && !queue[sType]) { this._uiEvtsInitQueue = queue[sType] = 1; this.after(RENDER, function() { this._createUIEvent(sType); delete this._uiEvtsInitQueue[sType]; }); } }, // Override of "on" from Base to facilitate the firing of Widget events // based on DOM events of the same name/type (e.g. "click", "mouseover"). // Temporary solution until we have the ability to listen to when // someone adds an event listener (bug 2528230) on: function (type) { this._initUIEvent(type); return Widget.superclass.on.apply(this, arguments); }, // Override of "publish" from Base to facilitate the firing of Widget events // based on DOM events of the same name/type (e.g. "click", "mouseover"). // Temporary solution until we have the ability to listen to when // someone publishes an event (bug 2528230) publish: function (type, config) { var sType = this._getUIEvent(type); if (sType && config && config.defaultFn) { this._initUIEvent(sType); } return Widget.superclass.publish.apply(this, arguments); } }, true); // overwrite existing EventTarget methods }, '3.5.1' ,{requires:['widget-base', 'node-event-delegate']});