/* YUI 3.17.2 (build 9c3c78e) Copyright 2014 Yahoo! Inc. All rights reserved. Licensed under the BSD License. http://yuilibrary.com/license/ */ YUI.add('attribute-core', function (Y, NAME) { /** * 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 item = this.data[name]; if (!item) { item = this.data[name] = {}; } item[key] = val; }, /** * Adds multiple properties to an item. * * @method addAll * @param name {String} The name of the item. * @param obj {Object} A hash of property/value pairs. */ addAll: function(name, obj) { var item = this.data[name], key; if (!item) { item = this.data[name] = {}; } for (key in obj) { if (obj.hasOwnProperty(key)) { item[key] = obj[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 item = this.data[name]; if (item) { delete item[key]; } }, /** * Removes multiple properties from an item, or removes the item completely. * * @method removeAll * @param name {String} The name of the item. * @param obj {Object|Array} Collection of properties to delete. If not provided, the entire item is removed. */ removeAll: function(name, obj) { var data; if (!obj) { data = this.data; if (name in data) { delete data[name]; } } else { Y.each(obj, function(value, key) { this.remove(name, typeof key === 'string' ? key : value); }, 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 item = this.data[name]; if (item) { return item[key]; } }, /** * 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 item = this.data[name], key, obj; if (reference) { obj = item; } else if (item) { obj = {}; for (key in item) { if (item.hasOwnProperty(key)) { obj[key] = item[key]; } } } return obj; } }; /*For log lines*/ /*jshint maxlen:200*/ /** * 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", INIT_VALUE = "initValue", LAZY = "lazy", 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 AttributeObservable * (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) { // HACK: 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._yuievt = null; 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]; /** * Utility method to protect an attribute configuration hash, by merging the * entire object and the individual attr config objects. * * @method protectAttrs * @static * @param {Object} attrs A hash of attribute to configuration object pairs. * @return {Object} A protected version of the `attrs` argument. */ AttributeCore.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; }; 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: *
* *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.
*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)
*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.
** 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.
** 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.
*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) { Y.log('Adding attribute: ' + name, 'info', 'attribute'); var host = this, // help compression state = host._state, data = state.data, value, added, hasValue; config = config || {}; if (LAZY_ADD in config) { lazy = config[LAZY_ADD]; } added = state.get(name, ADDED); if (lazy && !added) { state.data[name] = { lazy : config, added : true }; } else { if (added && !config.isLazyAdd) { Y.log('Attribute: ' + name + ' already exists. Cannot add it again without removing it first', 'warn', 'attribute'); } if (!added || config.isLazyAdd) { hasValue = (VALUE in config); if (config.readOnly && !hasValue) { Y.log('readOnly attribute: ' + name + ', added without an initial value. Value will be set on initial call to set', 'warn', 'attribute');} if (hasValue) { // We'll go through set, don't want to set value in config directly // PERF TODO: VALIDATE: See if setting this to undefined is sufficient. We use to delete before. // In certain code paths/use cases, undefined may not be the same as not present. // If not, we can set it to some known fixed value (like INVALID_VALUE, say INITIALIZING_VALUE) for performance, // to avoid a delete which seems to help a lot. value = config.value; config.value = undefined; } config.added = true; config.initializing = true; data[name] = config; if (hasValue) { // Go through set, so that raw values get normalized/validated host.set(name, value); } config.initializing = false; } } 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
* @param {Object} [lazyCfg] Optional config hash for the attribute. This is added for performance
* along the critical path, where the calling method has already obtained lazy config from state.
*/
_addLazyAttr: function(name, lazyCfg) {
var state = this._state;
lazyCfg = lazyCfg || state.get(name, LAZY);
if (lazyCfg) {
// PERF TODO: For App's id override, otherwise wouldn't be
// needed. It expects to find it in the cfg for it's
// addAttr override. Would like to remove, once App override is
// removed.
state.data[name].lazy = undefined;
lazyCfg.isLazyAdd = true;
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.
* @param {Object} [opts] Optional data providing the circumstances for the change.
* @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 data providing the circumstances for the change.
* @return {Object} A reference to the host object.
*/
_set : function(name, val, opts) {
return this._setAttr(name, val, opts, 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 data providing the circumstances for the change.
* @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) {
var allowSet = true,
state = this._state,
stateProxy = this._stateProxy,
tCfgs = this._tCfgs,
cfg,
initialSet,
strPath,
path,
currVal,
writeOnce,
initializing;
if (name.indexOf(DOT) !== -1) {
strPath = name;
path = name.split(DOT);
name = path.shift();
}
// On Demand - Should be rare - handles out of order valueFn, setter, getter references
if (tCfgs && tCfgs[name]) {
this._addOutOfOrder(name, tCfgs[name]);
}
cfg = state.data[name] || {};
if (cfg.lazy) {
cfg = cfg.lazy;
this._addLazyAttr(name, cfg);
}
initialSet = (cfg.value === undefined);
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) {
Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute');
allowSet = false;
}
if (cfg.readOnly) {
Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute');
allowSet = false;
}
}
if (!initializing && !force && writeOnce === INIT_ONLY) {
Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce: "initOnly"', 'warn', 'attribute');
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) {
Y.log('Set attribute path:' + strPath + ', aborted; Path is invalid', 'warn', 'attribute');
allowSet = false;
}
}
if (allowSet) {
if (!this._fireAttrChange || initializing) {
this._setAttrVal(name, strPath, currVal, val, opts, cfg);
} 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, cfg);
}
}
}
return this;
},
/**
* Utility method used by get/set to add attributes
* encountered out of order when calling addAttrs().
*
* For example, if:
*
* this.addAttrs({
* foo: {
* setter: function() {
* // make sure this bar is available when foo is added
* this.get("bar");
* }
* },
* bar: {
* value: ...
* }
* });
*
* @method _addOutOfOrder
* @private
* @param name {String} attribute name
* @param cfg {Object} attribute configuration
*/
_addOutOfOrder : function(name, cfg) {
var attrs = {};
attrs[name] = cfg;
delete this._tCfgs[name];
// TODO: The original code went through addAttrs, so
// sticking with it for this pass. Seems like we could
// just jump straight to _addAttr() and get some perf
// improvement.
this._addAttrs(attrs, this._tVals);
},
/**
* 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 fullName = name,
tCfgs = this._tCfgs,
path,
getter,
val,
attrCfg;
if (name.indexOf(DOT) !== -1) {
path = name.split(DOT);
name = path.shift();
}
// On Demand - Should be rare - handles out of
// order valueFn, setter, getter references
if (tCfgs && tCfgs[name]) {
this._addOutOfOrder(name, tCfgs[name]);
}
attrCfg = this._state.data[name] || {};
// Lazy Init
if (attrCfg.lazy) {
attrCfg = attrCfg.lazy;
this._addLazyAttr(name, attrCfg);
}
val = this._getStateVal(name, attrCfg);
getter = attrCfg.getter;
if (getter && !getter.call) {
getter = this[getter];
}
val = (getter) ? getter.call(this, 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
* @param {Object} [cfg] Optional config hash for the attribute. This is added for performance along the critical path,
* where the calling method has already obtained the config from state.
*
* @return {Any} The stored value of the attribute
*/
_getStateVal : function(name, cfg) {
var stateProxy = this._stateProxy;
if (!cfg) {
cfg = this._state.getAll(name) || {};
}
return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.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.
* @param {Object} [opts] Optional data providing the circumstances for the change.
* @param {Object} [attrCfg] Optional config hash for the attribute. This is added for performance along the critical path,
* where the calling method has already obtained the config from state.
*
* @return {Boolean} true if the new attribute value was stored, false if not.
*/
_setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) {
var host = this,
allowSet = true,
cfg = attrCfg || this._state.data[attrName] || {},
validator = cfg.validator,
setter = cfg.setter,
initializing = cfg.initializing,
prevRawVal = this._getStateVal(attrName, cfg),
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, opts);
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, opts);
if (retVal === INVALID_VALUE) {
if (initializing) {
Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal + ', initializing to default value', 'warn', 'attribute');
newVal = cfg.defaultValue;
} else {
Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal, 'warn', 'attribute');
allowSet = false;
}
} else if (retVal !== undefined){
Y.log('Attribute: ' + attrName + ', raw value: ' + newVal + ' modified by setter to:' + retVal, 'info', 'attribute');
newVal = retVal;
}
}
}
if (allowSet) {
if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) {
Y.log('Attribute: ' + attrName + ', value unchanged:' + newVal, 'warn', 'attribute');
allowSet = false;
} else {
// Store value
if (!(INIT_VALUE in cfg)) {
cfg.initValue = newVal;
}
host._setStateVal(attrName, newVal);
}
}
} else {
Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute');
allowSet = false;
}
return allowSet;
},
/**
* Sets multiple attribute values.
*
* @method setAttrs
* @param {Object} attrs An object with attributes name/value pairs.
* @param {Object} [opts] Optional data providing the circumstances for the change.
* @return {Object} A reference to the host object.
* @chainable
*/
setAttrs : function(attrs, opts) {
return this._setAttrs(attrs, opts);
},
/**
* Implementation behind the public setAttrs method, to set multiple attribute values.
*
* @method _setAttrs
* @protected
* @param {Object} attrs An object with attributes name/value pairs.
* @param {Object} [opts] Optional data providing the circumstances for the change
* @return {Object} A reference to the host object.
* @chainable
*/
_setAttrs : function(attrs, opts) {
var attr;
for (attr in attrs) {
if ( attrs.hasOwnProperty(attr) ) {
this.set(attr, attrs[attr], opts);
}
}
return this;
},
/**
* Gets multiple attribute values.
*
* @method getAttrs
* @param {String[]|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 {String[]|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 obj = {},
attr, i, len,
modifiedOnly = (attrs === true);
// TODO - figure out how to get all "added"
if (!attrs || modifiedOnly) {
attrs = O.keys(this._state.data);
}
for (i = 0, len = attrs.length; i < len; i++) {
attr = attrs[i];
if (!modifiedOnly || this._getStateVal(attr) != this._state.get(attr, INIT_VALUE)) {
// Go through get, to honor cloning/normalization
obj[attr] = this.get(attr);
}
}
return obj;
},
/**
* 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) { if (cfgs) { this._tCfgs = cfgs; this._tVals = (values) ? this._normAttrVals(values) : null; this._addAttrs(cfgs, this._tVals, lazy); this._tCfgs = this._tVals = null; } return this; }, /** * 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 tCfgs = this._tCfgs, tVals = this._tVals, 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 = this._getAttrInitVal(attr, attrCfg, tVals); if (value !== undefined) { attrCfg.value = value; } if (tCfgs[attr]) { tCfgs[attr] = undefined; } this.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. * @deprecated Use `AttributeCore.protectAttrs()` or * `Attribute.protectAttrs()` which are the same static utility method. */ _protectAttrs : AttributeCore.protectAttrs, /** * 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} 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) { return null; } vals = {}; for (k in valueHash) { if (valueHash.hasOwnProperty(k)) { if (k.indexOf(DOT) !== -1) { path = k.split(DOT); attr = path.shift(); subvals = subvals || {}; v = subvals[attr] = subvals[attr] || []; v[v.length] = { path : path, value: valueHash[k] }; } else { vals[k] = valueHash[k]; } } } return { simple:vals, complex:subvals }; }, /** * 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, tmpVal, initValSet = false, readOnly = cfg.readOnly, simple, complex, i, l, path, subval, subvals; if (!readOnly && initValues) { // Simple Attributes simple = initValues.simple; if (simple && simple.hasOwnProperty(attr)) { val = simple[attr]; initValSet = true; } } if (valFn && !initValSet) { if (!valFn.call) { valFn = this[valFn]; } if (valFn) { tmpVal = valFn.call(this, attr); val = tmpVal; } } if (!readOnly && initValues) { // Complex Attributes (complex values applied, after simple, in case both are set) complex = initValues.complex; if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) { 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; }, /** * 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(Y.AttributeCore.protectAttrs(attrs), values, lazy); } } }; Y.AttributeCore = AttributeCore; }, '3.17.2', {"requires": ["oop"]});