2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
8 YUI.add('attribute-core', function (Y, NAME) {
11 * The State class maintains state for a collection of named items, with
12 * a varying number of properties defined.
14 * It avoids the need to create a separate class for the item, and separate instances
15 * of these classes for each item, by storing the state in a 2 level hash table,
16 * improving performance when the number of items is likely to be large.
21 Y.State = function() {
32 * Adds a property to an item.
35 * @param name {String} The name of the item.
36 * @param key {String} The name of the property.
37 * @param val {Any} The value of the property.
39 add: function(name, key, val) {
40 var item = this.data[name];
43 item = this.data[name] = {};
50 * Adds multiple properties to an item.
53 * @param name {String} The name of the item.
54 * @param obj {Object} A hash of property/value pairs.
56 addAll: function(name, obj) {
57 var item = this.data[name],
61 item = this.data[name] = {};
65 if (obj.hasOwnProperty(key)) {
72 * Removes a property from an item.
75 * @param name {String} The name of the item.
76 * @param key {String} The property to remove.
78 remove: function(name, key) {
79 var item = this.data[name];
87 * Removes multiple properties from an item, or removes the item completely.
90 * @param name {String} The name of the item.
91 * @param obj {Object|Array} Collection of properties to delete. If not provided, the entire item is removed.
93 removeAll: function(name, obj) {
103 Y.each(obj, function(value, key) {
104 this.remove(name, typeof key === 'string' ? key : value);
110 * For a given item, returns the value of the property requested, or undefined if not found.
113 * @param name {String} The name of the item
114 * @param key {String} Optional. The property value to retrieve.
115 * @return {Any} The value of the supplied property.
117 get: function(name, key) {
118 var item = this.data[name];
126 * For the given item, returns an object with all of the
127 * item's property/value pairs. By default the object returned
128 * is a shallow copy of the stored data, but passing in true
129 * as the second parameter will return a reference to the stored
133 * @param name {String} The name of the item
134 * @param reference {boolean} true, if you want a reference to the stored
136 * @return {Object} An object with property/value pairs for the item.
138 getAll : function(name, reference) {
139 var item = this.data[name],
148 if (item.hasOwnProperty(key)) {
149 obj[key] = item[key];
158 /*jshint maxlen:200*/
161 * The attribute module provides an augmentable Attribute implementation, which
162 * adds configurable attributes and attribute change events to the class being
163 * augmented. It also provides a State class, which is used internally by Attribute,
164 * but can also be used independently to provide a name/property/value data structure to
171 * The attribute-core submodule provides the lightest level of attribute handling support
172 * without Attribute change events, or lesser used methods such as reset(), modifyAttrs(),
176 * @submodule attribute-core
183 // Externally configurable props
186 READ_ONLY = "readOnly",
187 WRITE_ONCE = "writeOnce",
188 INIT_ONLY = "initOnly",
189 VALIDATOR = "validator",
191 VALUE_FN = "valueFn",
192 LAZY_ADD = "lazyAdd",
194 // Used for internal state management
196 BYPASS_PROXY = "_bypassProxy",
197 INIT_VALUE = "initValue",
204 * AttributeCore provides the lightest level of configurable attribute support. It is designed to be
205 * augmented on to a host class, and provides the host with the ability to configure
206 * attributes to store and retrieve state, <strong>but without support for attribute change events</strong>.
208 * <p>For example, attributes added to the host can be configured:</p>
210 * <li>As read only.</li>
211 * <li>As write once.</li>
212 * <li>With a setter function, which can be used to manipulate
213 * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li>
214 * <li>With a getter function, which can be used to manipulate stored values,
215 * before they are returned by Attribute's <a href="#method_get">get</a> method.</li>
216 * <li>With a validator function, to validate values before they are stored.</li>
219 * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
220 * options available for attributes.</p>
222 * <p>Object/Classes based on AttributeCore can augment <a href="AttributeObservable.html">AttributeObservable</a>
223 * (with true for overwrite) and <a href="AttributeExtras.html">AttributeExtras</a> to add attribute event and
224 * additional, less commonly used attribute methods, such as `modifyAttr`, `removeAttr` and `reset`.</p>
226 * @class AttributeCore
227 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>).
228 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor.
229 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>).
230 * These are not merged/cloned. The caller is responsible for isolating user provided values if required.
231 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
233 function AttributeCore(attrs, values, lazy) {
234 // HACK: Fix #2531929
235 // 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.
236 // Too late in the release cycle to do anything about the core problem.
237 // 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).
240 this._initAttrHost(attrs, values, lazy);
244 * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
246 * <p>You can return this value from your setter if you wish to combine validator and setter
247 * functionality into a single setter function, which either returns the massaged value to be stored or
248 * AttributeCore.INVALID_VALUE to prevent invalid values from being stored.</p>
250 * @property INVALID_VALUE
255 AttributeCore.INVALID_VALUE = {};
256 INVALID_VALUE = AttributeCore.INVALID_VALUE;
259 * The list of properties which can be configured for
260 * each attribute (e.g. setter, getter, writeOnce etc.).
262 * This property is used internally as a whitelist for faster
265 * @property _ATTR_CFG
270 AttributeCore._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BYPASS_PROXY];
273 * Utility method to protect an attribute configuration hash, by merging the
274 * entire object and the individual attr config objects.
276 * @method protectAttrs
278 * @param {Object} attrs A hash of attribute to configuration object pairs.
279 * @return {Object} A protected version of the `attrs` argument.
281 AttributeCore.protectAttrs = function (attrs) {
283 attrs = Y.merge(attrs);
284 for (var attr in attrs) {
285 if (attrs.hasOwnProperty(attr)) {
286 attrs[attr] = Y.merge(attrs[attr]);
294 AttributeCore.prototype = {
297 * Constructor logic for attributes. Initializes the host state, and sets up the inital attributes passed to the
300 * @method _initAttrHost
301 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>).
302 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor.
303 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>).
304 * These are not merged/cloned. The caller is responsible for isolating user provided values if required.
305 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
308 _initAttrHost : function(attrs, values, lazy) {
309 this._state = new Y.State();
310 this._initAttrs(attrs, values, lazy);
315 * Adds an attribute with the provided configuration to the host object.
318 * The config argument object supports the following properties:
322 * <dt>value <Any></dt>
323 * <dd>The initial value to set on the attribute</dd>
325 * <dt>valueFn <Function | String></dt>
327 * <p>A function, which will return the initial value to set on the attribute. This is useful
328 * for cases where the attribute configuration is defined statically, but needs to
329 * reference the host instance ("this") to obtain an initial value. If both the value and valueFn properties are defined,
330 * the value returned by the valueFn has precedence over the value property, unless it returns undefined, in which
331 * case the value property is used.</p>
333 * <p>valueFn can also be set to a string, representing the name of the instance method to be used to retrieve the value.</p>
336 * <dt>readOnly <boolean></dt>
337 * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
338 * cannot be modified by invoking the set method.</dd>
340 * <dt>writeOnce <boolean> or <string></dt>
342 * Whether or not the attribute is "write once". Attributes having writeOnce set to true,
343 * can only have their values set once, be it through the default configuration,
344 * constructor configuration arguments, or by invoking set.
345 * <p>The writeOnce attribute can also be set to the string "initOnly",
346 * in which case the attribute can only be set during initialization
347 * (when used with Base, this means it can only be set during construction)</p>
350 * <dt>setter <Function | String></dt>
352 * <p>The setter function used to massage or normalize the value passed to the set method for the attribute.
353 * The value returned by the setter will be the final stored value. Returning
354 * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
355 * the value from being stored.
358 * <p>setter can also be set to a string, representing the name of the instance method to be used as the setter function.</p>
361 * <dt>getter <Function | String></dt>
364 * The getter function used to massage or normalize the value returned by the get method for the attribute.
365 * The value returned by the getter function is the value which will be returned to the user when they
369 * <p>getter can also be set to a string, representing the name of the instance method to be used as the getter function.</p>
372 * <dt>validator <Function | String></dt>
375 * The validator function invoked prior to setting the stored value. Returning
376 * false from the validator function will prevent the value from being stored.
379 * <p>validator can also be set to a string, representing the name of the instance method to be used as the validator function.</p>
382 * <dt>lazyAdd <boolean></dt>
383 * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
384 * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
385 * the <a href="#method_addAttrs">addAttrs</a> method.</dd>
389 * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
390 * the context ("this") set to the host object.</p>
392 * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute,
393 * and are not intended for public use.</p>
397 * @param {String} name The name of the attribute.
398 * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
401 * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
402 * to protect the original values, you will need to merge the object.
405 * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
407 * @return {Object} A reference to the host object.
411 addAttr : function(name, config, lazy) {
413 Y.log('Adding attribute: ' + name, 'info', 'attribute');
415 var host = this, // help compression
422 config = config || {};
424 if (LAZY_ADD in config) {
425 lazy = config[LAZY_ADD];
428 added = state.get(name, ADDED);
430 if (lazy && !added) {
437 if (added && !config.isLazyAdd) { Y.log('Attribute: ' + name + ' already exists. Cannot add it again without removing it first', 'warn', 'attribute'); }
439 if (!added || config.isLazyAdd) {
441 hasValue = (VALUE in config);
443 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');}
447 // We'll go through set, don't want to set value in config directly
449 // PERF TODO: VALIDATE: See if setting this to undefined is sufficient. We use to delete before.
450 // In certain code paths/use cases, undefined may not be the same as not present.
451 // If not, we can set it to some known fixed value (like INVALID_VALUE, say INITIALIZING_VALUE) for performance,
452 // to avoid a delete which seems to help a lot.
454 value = config.value;
455 config.value = undefined;
459 config.initializing = true;
464 // Go through set, so that raw values get normalized/validated
465 host.set(name, value);
468 config.initializing = false;
476 * Checks if the given attribute has been added to the host
479 * @param {String} name The name of the attribute to check.
480 * @return {boolean} true if an attribute with the given name has been added, false if it hasn't.
481 * This method will return true for lazily added attributes.
483 attrAdded: function(name) {
484 return !!(this._state.get(name, ADDED));
488 * Returns the current value of the attribute. If the attribute
489 * has been configured with a 'getter' function, this method will delegate
490 * to the 'getter' to obtain the value of the attribute.
494 * @param {String} name The name of the attribute. If the value of the attribute is an Object,
495 * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
497 * @return {Any} The value of the attribute
499 get : function(name) {
500 return this._getAttr(name);
504 * Checks whether or not the attribute is one which has been
505 * added lazily and still requires initialization.
507 * @method _isLazyAttr
509 * @param {String} name The name of the attribute
510 * @return {boolean} true if it's a lazily added attribute, false otherwise.
512 _isLazyAttr: function(name) {
513 return this._state.get(name, LAZY);
517 * Finishes initializing an attribute which has been lazily added.
519 * @method _addLazyAttr
521 * @param {Object} name The name of the attribute
522 * @param {Object} [lazyCfg] Optional config hash for the attribute. This is added for performance
523 * along the critical path, where the calling method has already obtained lazy config from state.
525 _addLazyAttr: function(name, lazyCfg) {
526 var state = this._state;
528 lazyCfg = lazyCfg || state.get(name, LAZY);
532 // PERF TODO: For App's id override, otherwise wouldn't be
533 // needed. It expects to find it in the cfg for it's
534 // addAttr override. Would like to remove, once App override is
536 state.data[name].lazy = undefined;
538 lazyCfg.isLazyAdd = true;
540 this.addAttr(name, lazyCfg);
545 * Sets the value of an attribute.
550 * @param {String} name The name of the attribute. If the
551 * current value of the attribute is an Object, dot notation can be used
552 * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
553 * @param {Any} value The value to set the attribute to.
554 * @param {Object} [opts] Optional data providing the circumstances for the change.
555 * @return {Object} A reference to the host object.
557 set : function(name, val, opts) {
558 return this._setAttr(name, val, opts);
562 * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
568 * @param {String} name The name of the attribute.
569 * @param {Any} val The value to set the attribute to.
570 * @param {Object} [opts] Optional data providing the circumstances for the change.
571 * @return {Object} A reference to the host object.
573 _set : function(name, val, opts) {
574 return this._setAttr(name, val, opts, true);
578 * Provides the common implementation for the public set and protected _set methods.
580 * See <a href="#method_set">set</a> for argument details.
586 * @param {String} name The name of the attribute.
587 * @param {Any} value The value to set the attribute to.
588 * @param {Object} [opts] Optional data providing the circumstances for the change.
589 * @param {boolean} force If true, allows the caller to set values for
590 * readOnly or writeOnce attributes which have already been set.
592 * @return {Object} A reference to the host object.
594 _setAttr : function(name, val, opts, force) {
597 stateProxy = this._stateProxy,
607 if (name.indexOf(DOT) !== -1) {
610 path = name.split(DOT);
614 // On Demand - Should be rare - handles out of order valueFn, setter, getter references
615 if (tCfgs && tCfgs[name]) {
616 this._addOutOfOrder(name, tCfgs[name]);
619 cfg = state.data[name] || {};
623 this._addLazyAttr(name, cfg);
626 initialSet = (cfg.value === undefined);
628 if (stateProxy && name in stateProxy && !cfg._bypassProxy) {
629 // 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?
633 writeOnce = cfg.writeOnce;
634 initializing = cfg.initializing;
636 if (!initialSet && !force) {
639 Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute');
644 Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute');
649 if (!initializing && !force && writeOnce === INIT_ONLY) {
650 Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce: "initOnly"', 'warn', 'attribute');
655 // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
657 currVal = this.get(name);
661 val = O.setValue(Y.clone(currVal), path, val);
663 if (val === undefined) {
664 Y.log('Set attribute path:' + strPath + ', aborted; Path is invalid', 'warn', 'attribute');
670 if (!this._fireAttrChange || initializing) {
671 this._setAttrVal(name, strPath, currVal, val, opts, cfg);
673 // HACK - no real reason core needs to know about _fireAttrChange, but
674 // it adds fn hops if we want to break it out. Not sure it's worth it for this critical path
675 this._fireAttrChange(name, strPath, currVal, val, opts, cfg);
684 * Utility method used by get/set to add attributes
685 * encountered out of order when calling addAttrs().
691 * setter: function() {
692 * // make sure this bar is available when foo is added
701 * @method _addOutOfOrder
703 * @param name {String} attribute name
704 * @param cfg {Object} attribute configuration
706 _addOutOfOrder : function(name, cfg) {
711 delete this._tCfgs[name];
713 // TODO: The original code went through addAttrs, so
714 // sticking with it for this pass. Seems like we could
715 // just jump straight to _addAttr() and get some perf
717 this._addAttrs(attrs, this._tVals);
721 * Provides the common implementation for the public get method,
722 * allowing Attribute hosts to over-ride either method.
724 * See <a href="#method_get">get</a> for argument details.
730 * @param {String} name The name of the attribute.
731 * @return {Any} The value of the attribute.
733 _getAttr : function(name) {
741 if (name.indexOf(DOT) !== -1) {
742 path = name.split(DOT);
746 // On Demand - Should be rare - handles out of
747 // order valueFn, setter, getter references
748 if (tCfgs && tCfgs[name]) {
749 this._addOutOfOrder(name, tCfgs[name]);
752 attrCfg = this._state.data[name] || {};
756 attrCfg = attrCfg.lazy;
757 this._addLazyAttr(name, attrCfg);
760 val = this._getStateVal(name, attrCfg);
762 getter = attrCfg.getter;
764 if (getter && !getter.call) {
765 getter = this[getter];
768 val = (getter) ? getter.call(this, val, fullName) : val;
769 val = (path) ? O.getValue(val, path) : val;
775 * Gets the stored value for the attribute, from either the
776 * internal state object, or the state proxy if it exits
778 * @method _getStateVal
780 * @param {String} name The name of the attribute
781 * @param {Object} [cfg] Optional config hash for the attribute. This is added for performance along the critical path,
782 * where the calling method has already obtained the config from state.
784 * @return {Any} The stored value of the attribute
786 _getStateVal : function(name, cfg) {
787 var stateProxy = this._stateProxy;
790 cfg = this._state.getAll(name) || {};
793 return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value;
797 * Sets the stored value for the attribute, in either the
798 * internal state object, or the state proxy if it exits
800 * @method _setStateVal
802 * @param {String} name The name of the attribute
803 * @param {Any} value The value of the attribute
805 _setStateVal : function(name, value) {
806 var stateProxy = this._stateProxy;
807 if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
808 stateProxy[name] = value;
810 this._state.add(name, VALUE, value);
815 * Updates the stored value of the attribute in the privately held State object,
816 * if validation and setter passes.
818 * @method _setAttrVal
820 * @param {String} attrName The attribute name.
821 * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
822 * @param {Any} prevVal The currently stored value of the attribute.
823 * @param {Any} newVal The value which is going to be stored.
824 * @param {Object} [opts] Optional data providing the circumstances for the change.
825 * @param {Object} [attrCfg] Optional config hash for the attribute. This is added for performance along the critical path,
826 * where the calling method has already obtained the config from state.
828 * @return {booolean} true if the new attribute value was stored, false if not.
830 _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) {
834 cfg = attrCfg || this._state.data[attrName] || {},
835 validator = cfg.validator,
837 initializing = cfg.initializing,
838 prevRawVal = this._getStateVal(attrName, cfg),
839 name = subAttrName || attrName,
844 if (!validator.call) {
845 // Assume string - trying to keep critical path tight, so avoiding Lang check
846 validator = this[validator];
849 valid = validator.call(host, newVal, name, opts);
851 if (!valid && initializing) {
852 newVal = cfg.defaultValue;
853 valid = true; // Assume it's valid, for perf.
858 if (!validator || valid) {
861 // Assume string - trying to keep critical path tight, so avoiding Lang check
862 setter = this[setter];
865 retVal = setter.call(host, newVal, name, opts);
867 if (retVal === INVALID_VALUE) {
869 Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal + ', initializing to default value', 'warn', 'attribute');
870 newVal = cfg.defaultValue;
872 Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal, 'warn', 'attribute');
875 } else if (retVal !== undefined){
876 Y.log('Attribute: ' + attrName + ', raw value: ' + newVal + ' modified by setter to:' + retVal, 'info', 'attribute');
883 if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) {
884 Y.log('Attribute: ' + attrName + ', value unchanged:' + newVal, 'warn', 'attribute');
888 if (!(INIT_VALUE in cfg)) {
889 cfg.initValue = newVal;
891 host._setStateVal(attrName, newVal);
896 Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute');
904 * Sets multiple attribute values.
907 * @param {Object} attrs An object with attributes name/value pairs.
908 * @param {Object} [opts] Optional data providing the circumstances for the change.
909 * @return {Object} A reference to the host object.
912 setAttrs : function(attrs, opts) {
913 return this._setAttrs(attrs, opts);
917 * Implementation behind the public setAttrs method, to set multiple attribute values.
921 * @param {Object} attrs An object with attributes name/value pairs.
922 * @param {Object} [opts] Optional data providing the circumstances for the change
923 * @return {Object} A reference to the host object.
926 _setAttrs : function(attrs, opts) {
928 for (attr in attrs) {
929 if ( attrs.hasOwnProperty(attr) ) {
930 this.set(attr, attrs[attr], opts);
937 * Gets multiple attribute values.
940 * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
941 * returned. If set to true, all attributes modified from their initial values are returned.
942 * @return {Object} An object with attribute name/value pairs.
944 getAttrs : function(attrs) {
945 return this._getAttrs(attrs);
949 * Implementation behind the public getAttrs method, to get multiple attribute values.
953 * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
954 * returned. If set to true, all attributes modified from their initial values are returned.
955 * @return {Object} An object with attribute name/value pairs.
957 _getAttrs : function(attrs) {
960 modifiedOnly = (attrs === true);
962 // TODO - figure out how to get all "added"
963 if (!attrs || modifiedOnly) {
964 attrs = O.keys(this._state.data);
967 for (i = 0, len = attrs.length; i < len; i++) {
970 if (!modifiedOnly || this._getStateVal(attr) != this._state.get(attr, INIT_VALUE)) {
971 // Go through get, to honor cloning/normalization
972 obj[attr] = this.get(attr);
980 * Configures a group of attributes, and sets initial values.
983 * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
984 * The caller is responsible for merging/cloning the configuration object if required.
990 * @param {Object} cfgs An object with attribute name/configuration pairs.
991 * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
992 * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
993 * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
994 * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
995 * See <a href="#method_addAttr">addAttr</a>.
997 * @return {Object} A reference to the host object.
999 addAttrs : function(cfgs, values, lazy) {
1002 this._tVals = (values) ? this._normAttrVals(values) : null;
1003 this._addAttrs(cfgs, this._tVals, lazy);
1004 this._tCfgs = this._tVals = null;
1011 * Implementation behind the public addAttrs method.
1013 * This method is invoked directly by get if it encounters a scenario
1014 * in which an attribute's valueFn attempts to obtain the
1015 * value an attribute in the same group of attributes, which has not yet
1016 * been added (on demand initialization).
1020 * @param {Object} cfgs An object with attribute name/configuration pairs.
1021 * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
1022 * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
1023 * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
1024 * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
1025 * See <a href="#method_addAttr">addAttr</a>.
1027 _addAttrs : function(cfgs, values, lazy) {
1028 var tCfgs = this._tCfgs,
1029 tVals = this._tVals,
1034 for (attr in cfgs) {
1035 if (cfgs.hasOwnProperty(attr)) {
1037 // Not Merging. Caller is responsible for isolating configs
1038 attrCfg = cfgs[attr];
1039 attrCfg.defaultValue = attrCfg.value;
1041 // Handle simple, complex and user values, accounting for read-only
1042 value = this._getAttrInitVal(attr, attrCfg, tVals);
1044 if (value !== undefined) {
1045 attrCfg.value = value;
1049 tCfgs[attr] = undefined;
1052 this.addAttr(attr, attrCfg, lazy);
1058 * Utility method to protect an attribute configuration
1059 * hash, by merging the entire object and the individual
1060 * attr config objects.
1062 * @method _protectAttrs
1064 * @param {Object} attrs A hash of attribute to configuration object pairs.
1065 * @return {Object} A protected version of the attrs argument.
1066 * @deprecated Use `AttributeCore.protectAttrs()` or
1067 * `Attribute.protectAttrs()` which are the same static utility method.
1069 _protectAttrs : AttributeCore.protectAttrs,
1072 * Utility method to normalize attribute values. The base implementation
1073 * simply merges the hash to protect the original.
1075 * @method _normAttrVals
1076 * @param {Object} valueHash An object with attribute name/value pairs
1078 * @return {Object} An object literal with 2 properties - "simple" and "complex",
1079 * containing simple and complex attribute values respectively keyed
1080 * by the top level attribute name, or null, if valueHash is falsey.
1084 _normAttrVals : function(valueHash) {
1097 for (k in valueHash) {
1098 if (valueHash.hasOwnProperty(k)) {
1099 if (k.indexOf(DOT) !== -1) {
1100 path = k.split(DOT);
1101 attr = path.shift();
1103 subvals = subvals || {};
1105 v = subvals[attr] = subvals[attr] || [];
1111 vals[k] = valueHash[k];
1116 return { simple:vals, complex:subvals };
1120 * Returns the initial value of the given attribute from
1121 * either the default configuration provided, or the
1122 * over-ridden value if it exists in the set of initValues
1123 * provided and the attribute is not read-only.
1125 * @param {String} attr The name of the attribute
1126 * @param {Object} cfg The attribute configuration object
1127 * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
1129 * @return {Any} The initial value of the attribute.
1131 * @method _getAttrInitVal
1134 _getAttrInitVal : function(attr, cfg, initValues) {
1135 var val = cfg.value,
1136 valFn = cfg.valueFn,
1139 readOnly = cfg.readOnly,
1148 if (!readOnly && initValues) {
1149 // Simple Attributes
1150 simple = initValues.simple;
1151 if (simple && simple.hasOwnProperty(attr)) {
1157 if (valFn && !initValSet) {
1159 valFn = this[valFn];
1162 tmpVal = valFn.call(this, attr);
1167 if (!readOnly && initValues) {
1169 // Complex Attributes (complex values applied, after simple, in case both are set)
1170 complex = initValues.complex;
1172 if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) {
1173 subvals = complex[attr];
1174 for (i = 0, l = subvals.length; i < l; ++i) {
1175 path = subvals[i].path;
1176 subval = subvals[i].value;
1177 O.setValue(val, path, subval);
1186 * Utility method to set up initial attributes defined during construction,
1187 * either through the constructor.ATTRS property, or explicitly passed in.
1189 * @method _initAttrs
1191 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>).
1192 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor.
1193 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>).
1194 * These are not merged/cloned. The caller is responsible for isolating user provided values if required.
1195 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
1197 _initAttrs : function(attrs, values, lazy) {
1198 // ATTRS support for Node, which is not Base based
1199 attrs = attrs || this.constructor.ATTRS;
1202 BaseCore = Y.BaseCore,
1203 baseInst = (Base && Y.instanceOf(this, Base)),
1204 baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore));
1206 if (attrs && !baseInst && !baseCoreInst) {
1207 this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy);
1212 Y.AttributeCore = AttributeCore;
1215 }, '3.13.0', {"requires": ["oop"]});