NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / attribute-core / attribute-core.js
blob8f82b77c4597edcff0c35c5ca0b2b37da614d9c9
1 /*
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/
6 */
8 YUI.add('attribute-core', function (Y, NAME) {
10     /**
11      * The State class maintains state for a collection of named items, with
12      * a varying number of properties defined.
13      *
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.
17      *
18      * @constructor
19      * @class State
20      */
21     Y.State = function() {
22         /**
23          * Hash of attributes
24          * @property data
25          */
26         this.data = {};
27     };
29     Y.State.prototype = {
31         /**
32          * Adds a property to an item.
33          *
34          * @method add
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.
38          */
39         add: function(name, key, val) {
40             var item = this.data[name];
42             if (!item) {
43                 item = this.data[name] = {};
44             }
46             item[key] = val;
47         },
49         /**
50          * Adds multiple properties to an item.
51          *
52          * @method addAll
53          * @param name {String} The name of the item.
54          * @param obj {Object} A hash of property/value pairs.
55          */
56         addAll: function(name, obj) {
57             var item = this.data[name],
58                 key;
60             if (!item) {
61                 item = this.data[name] = {};
62             }
64             for (key in obj) {
65                 if (obj.hasOwnProperty(key)) {
66                     item[key] = obj[key];
67                 }
68             }
69         },
71         /**
72          * Removes a property from an item.
73          *
74          * @method remove
75          * @param name {String} The name of the item.
76          * @param key {String} The property to remove.
77          */
78         remove: function(name, key) {
79             var item = this.data[name];
81             if (item) {
82                 delete item[key];
83             }
84         },
86         /**
87          * Removes multiple properties from an item, or removes the item completely.
88          *
89          * @method removeAll
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.
92          */
93         removeAll: function(name, obj) {
94             var data;
96             if (!obj) {
97                 data = this.data;
99                 if (name in data) {
100                     delete data[name];
101                 }
102             } else {
103                 Y.each(obj, function(value, key) {
104                     this.remove(name, typeof key === 'string' ? key : value);
105                 }, this);
106             }
107         },
109         /**
110          * For a given item, returns the value of the property requested, or undefined if not found.
111          *
112          * @method get
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.
116          */
117         get: function(name, key) {
118             var item = this.data[name];
120             if (item) {
121                 return item[key];
122             }
123         },
125         /**
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
130          * data.
131          *
132          * @method getAll
133          * @param name {String} The name of the item
134          * @param reference {boolean} true, if you want a reference to the stored
135          * object
136          * @return {Object} An object with property/value pairs for the item.
137          */
138         getAll : function(name, reference) {
139             var item = this.data[name],
140                 key, obj;
142             if (reference) {
143                 obj = item;
144             } else if (item) {
145                 obj = {};
147                 for (key in item) {
148                     if (item.hasOwnProperty(key)) {
149                         obj[key] = item[key];
150                     }
151                 }
152             }
154             return obj;
155         }
156     };
157     /*For log lines*/
158     /*jshint maxlen:200*/
160     /**
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
165      * store state.
166      *
167      * @module attribute
168      */
170     /**
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(),
173      * and removeAttr().
174      *
175      * @module attribute
176      * @submodule attribute-core
177      */
178     var O = Y.Object,
179         Lang = Y.Lang,
181         DOT = ".",
183         // Externally configurable props
184         GETTER = "getter",
185         SETTER = "setter",
186         READ_ONLY = "readOnly",
187         WRITE_ONCE = "writeOnce",
188         INIT_ONLY = "initOnly",
189         VALIDATOR = "validator",
190         VALUE = "value",
191         VALUE_FN = "valueFn",
192         LAZY_ADD = "lazyAdd",
194         // Used for internal state management
195         ADDED = "added",
196         BYPASS_PROXY = "_bypassProxy",
197         INIT_VALUE = "initValue",
198         LAZY = "lazy",
200         INVALID_VALUE;
202     /**
203      * <p>
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>.
207      * </p>
208      * <p>For example, attributes added to the host can be configured:</p>
209      * <ul>
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>
217      * </ul>
218      *
219      * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
220      * options available for attributes.</p>
221      *
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>
225      *
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>).
232      */
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).
238         this._yuievt = null;
240         this._initAttrHost(attrs, values, lazy);
241     }
243     /**
244      * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
245      *
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>
249      *
250      * @property INVALID_VALUE
251      * @type Object
252      * @static
253      * @final
254      */
255     AttributeCore.INVALID_VALUE = {};
256     INVALID_VALUE = AttributeCore.INVALID_VALUE;
258     /**
259      * The list of properties which can be configured for
260      * each attribute (e.g. setter, getter, writeOnce etc.).
261      *
262      * This property is used internally as a whitelist for faster
263      * Y.mix operations.
264      *
265      * @property _ATTR_CFG
266      * @type Array
267      * @static
268      * @protected
269      */
270     AttributeCore._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BYPASS_PROXY];
272     /**
273      * Utility method to protect an attribute configuration hash, by merging the
274      * entire object and the individual attr config objects.
275      *
276      * @method protectAttrs
277      * @static
278      * @param {Object} attrs A hash of attribute to configuration object pairs.
279      * @return {Object} A protected version of the `attrs` argument.
280      */
281     AttributeCore.protectAttrs = function (attrs) {
282         if (attrs) {
283             attrs = Y.merge(attrs);
284             for (var attr in attrs) {
285                 if (attrs.hasOwnProperty(attr)) {
286                     attrs[attr] = Y.merge(attrs[attr]);
287                 }
288             }
289         }
291         return attrs;
292     };
294     AttributeCore.prototype = {
296         /**
297          * Constructor logic for attributes. Initializes the host state, and sets up the inital attributes passed to the
298          * constructor.
299          *
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>).
306          * @private
307          */
308         _initAttrHost : function(attrs, values, lazy) {
309             this._state = new Y.State();
310             this._initAttrs(attrs, values, lazy);
311         },
313         /**
314          * <p>
315          * Adds an attribute with the provided configuration to the host object.
316          * </p>
317          * <p>
318          * The config argument object supports the following properties:
319          * </p>
320          *
321          * <dl>
322          *    <dt>value &#60;Any&#62;</dt>
323          *    <dd>The initial value to set on the attribute</dd>
324          *
325          *    <dt>valueFn &#60;Function | String&#62;</dt>
326          *    <dd>
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>
332          *
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>
334          *    </dd>
335          *
336          *    <dt>readOnly &#60;boolean&#62;</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>
339          *
340          *    <dt>writeOnce &#60;boolean&#62; or &#60;string&#62;</dt>
341          *    <dd>
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>
348          *    </dd>
349          *
350          *    <dt>setter &#60;Function | String&#62;</dt>
351          *    <dd>
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.
356          *    </p>
357          *
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>
359          *    </dd>
360          *
361          *    <dt>getter &#60;Function | String&#62;</dt>
362          *    <dd>
363          *    <p>
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
366          *    invoke get.
367          *    </p>
368          *
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>
370          *    </dd>
371          *
372          *    <dt>validator &#60;Function | String&#62;</dt>
373          *    <dd>
374          *    <p>
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.
377          *    </p>
378          *
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>
380          *    </dd>
381          *
382          *    <dt>lazyAdd &#60;boolean&#62;</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>
386          *
387          * </dl>
388          *
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>
391          *
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>
394          *
395          * @method addAttr
396          *
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.
399          *
400          * <p>
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.
403          * </p>
404          *
405          * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
406          *
407          * @return {Object} A reference to the host object.
408          *
409          * @chainable
410          */
411         addAttr : function(name, config, lazy) {
414             var host = this, // help compression
415                 state = host._state,
416                 data = state.data,
417                 value,
418                 added,
419                 hasValue;
421             config = config || {};
423             if (LAZY_ADD in config) {
424                 lazy = config[LAZY_ADD];
425             }
427             added = state.get(name, ADDED);
429             if (lazy && !added) {
430                 state.data[name] = {
431                     lazy : config,
432                     added : true
433                 };
434             } else {
437                 if (!added || config.isLazyAdd) {
439                     hasValue = (VALUE in config);
442                     if (hasValue) {
444                         // We'll go through set, don't want to set value in config directly
446                         // PERF TODO: VALIDATE: See if setting this to undefined is sufficient. We use to delete before.
447                         // In certain code paths/use cases, undefined may not be the same as not present.
448                         // If not, we can set it to some known fixed value (like INVALID_VALUE, say INITIALIZING_VALUE) for performance,
449                         // to avoid a delete which seems to help a lot.
451                         value = config.value;
452                         config.value = undefined;
453                     }
455                     config.added = true;
456                     config.initializing = true;
458                     data[name] = config;
460                     if (hasValue) {
461                         // Go through set, so that raw values get normalized/validated
462                         host.set(name, value);
463                     }
465                     config.initializing = false;
466                 }
467             }
469             return host;
470         },
472         /**
473          * Checks if the given attribute has been added to the host
474          *
475          * @method attrAdded
476          * @param {String} name The name of the attribute to check.
477          * @return {boolean} true if an attribute with the given name has been added, false if it hasn't.
478          *         This method will return true for lazily added attributes.
479          */
480         attrAdded: function(name) {
481             return !!(this._state.get(name, ADDED));
482         },
484         /**
485          * Returns the current value of the attribute. If the attribute
486          * has been configured with a 'getter' function, this method will delegate
487          * to the 'getter' to obtain the value of the attribute.
488          *
489          * @method get
490          *
491          * @param {String} name The name of the attribute. If the value of the attribute is an Object,
492          * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
493          *
494          * @return {Any} The value of the attribute
495          */
496         get : function(name) {
497             return this._getAttr(name);
498         },
500         /**
501          * Checks whether or not the attribute is one which has been
502          * added lazily and still requires initialization.
503          *
504          * @method _isLazyAttr
505          * @private
506          * @param {String} name The name of the attribute
507          * @return {boolean} true if it's a lazily added attribute, false otherwise.
508          */
509         _isLazyAttr: function(name) {
510             return this._state.get(name, LAZY);
511         },
513         /**
514          * Finishes initializing an attribute which has been lazily added.
515          *
516          * @method _addLazyAttr
517          * @private
518          * @param {Object} name The name of the attribute
519          * @param {Object} [lazyCfg] Optional config hash for the attribute. This is added for performance
520          * along the critical path, where the calling method has already obtained lazy config from state.
521          */
522         _addLazyAttr: function(name, lazyCfg) {
523             var state = this._state;
525             lazyCfg = lazyCfg || state.get(name, LAZY);
527             if (lazyCfg) {
529                 // PERF TODO: For App's id override, otherwise wouldn't be
530                 // needed. It expects to find it in the cfg for it's
531                 // addAttr override. Would like to remove, once App override is
532                 // removed.
533                 state.data[name].lazy = undefined;
535                 lazyCfg.isLazyAdd = true;
537                 this.addAttr(name, lazyCfg);
538             }
539         },
541         /**
542          * Sets the value of an attribute.
543          *
544          * @method set
545          * @chainable
546          *
547          * @param {String} name The name of the attribute. If the
548          * current value of the attribute is an Object, dot notation can be used
549          * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
550          * @param {Any} value The value to set the attribute to.
551          * @param {Object} [opts] Optional data providing the circumstances for the change.
552          * @return {Object} A reference to the host object.
553          */
554         set : function(name, val, opts) {
555             return this._setAttr(name, val, opts);
556         },
558         /**
559          * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
560          *
561          * @method _set
562          * @protected
563          * @chainable
564          *
565          * @param {String} name The name of the attribute.
566          * @param {Any} val The value to set the attribute to.
567          * @param {Object} [opts] Optional data providing the circumstances for the change.
568          * @return {Object} A reference to the host object.
569          */
570         _set : function(name, val, opts) {
571             return this._setAttr(name, val, opts, true);
572         },
574         /**
575          * Provides the common implementation for the public set and protected _set methods.
576          *
577          * See <a href="#method_set">set</a> for argument details.
578          *
579          * @method _setAttr
580          * @protected
581          * @chainable
582          *
583          * @param {String} name The name of the attribute.
584          * @param {Any} value The value to set the attribute to.
585          * @param {Object} [opts] Optional data providing the circumstances for the change.
586          * @param {boolean} force If true, allows the caller to set values for
587          * readOnly or writeOnce attributes which have already been set.
588          *
589          * @return {Object} A reference to the host object.
590          */
591         _setAttr : function(name, val, opts, force)  {
592             var allowSet = true,
593                 state = this._state,
594                 stateProxy = this._stateProxy,
595                 tCfgs = this._tCfgs,
596                 cfg,
597                 initialSet,
598                 strPath,
599                 path,
600                 currVal,
601                 writeOnce,
602                 initializing;
604             if (name.indexOf(DOT) !== -1) {
605                 strPath = name;
607                 path = name.split(DOT);
608                 name = path.shift();
609             }
611             // On Demand - Should be rare - handles out of order valueFn, setter, getter references
612             if (tCfgs && tCfgs[name]) {
613                 this._addOutOfOrder(name, tCfgs[name]);
614             }
616             cfg = state.data[name] || {};
618             if (cfg.lazy) {
619                 cfg = cfg.lazy;
620                 this._addLazyAttr(name, cfg);
621             }
623             initialSet = (cfg.value === undefined);
625             if (stateProxy && name in stateProxy && !cfg._bypassProxy) {
626                 // 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?
627                 initialSet = false;
628             }
630             writeOnce = cfg.writeOnce;
631             initializing = cfg.initializing;
633             if (!initialSet && !force) {
635                 if (writeOnce) {
636                     allowSet = false;
637                 }
639                 if (cfg.readOnly) {
640                     allowSet = false;
641                 }
642             }
644             if (!initializing && !force && writeOnce === INIT_ONLY) {
645                 allowSet = false;
646             }
648             if (allowSet) {
649                 // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
650                 if (!initialSet) {
651                     currVal =  this.get(name);
652                 }
654                 if (path) {
655                    val = O.setValue(Y.clone(currVal), path, val);
657                    if (val === undefined) {
658                        allowSet = false;
659                    }
660                 }
662                 if (allowSet) {
663                     if (!this._fireAttrChange || initializing) {
664                         this._setAttrVal(name, strPath, currVal, val, opts, cfg);
665                     } else {
666                         // HACK - no real reason core needs to know about _fireAttrChange, but
667                         // it adds fn hops if we want to break it out. Not sure it's worth it for this critical path
668                         this._fireAttrChange(name, strPath, currVal, val, opts, cfg);
669                     }
670                 }
671             }
673             return this;
674         },
676         /**
677          * Utility method used by get/set to add attributes
678          * encountered out of order when calling addAttrs().
679          *
680          * For example, if:
681          *
682          *     this.addAttrs({
683          *          foo: {
684          *              setter: function() {
685          *                 // make sure this bar is available when foo is added
686          *                 this.get("bar");
687          *              }
688          *          },
689          *          bar: {
690          *              value: ...
691          *          }
692          *     });
693          *
694          * @method _addOutOfOrder
695          * @private
696          * @param name {String} attribute name
697          * @param cfg {Object} attribute configuration
698          */
699         _addOutOfOrder : function(name, cfg) {
701             var attrs = {};
702             attrs[name] = cfg;
704             delete this._tCfgs[name];
706             // TODO: The original code went through addAttrs, so
707             // sticking with it for this pass. Seems like we could
708             // just jump straight to _addAttr() and get some perf
709             // improvement.
710             this._addAttrs(attrs, this._tVals);
711         },
713         /**
714          * Provides the common implementation for the public get method,
715          * allowing Attribute hosts to over-ride either method.
716          *
717          * See <a href="#method_get">get</a> for argument details.
718          *
719          * @method _getAttr
720          * @protected
721          * @chainable
722          *
723          * @param {String} name The name of the attribute.
724          * @return {Any} The value of the attribute.
725          */
726         _getAttr : function(name) {
727             var fullName = name,
728                 tCfgs = this._tCfgs,
729                 path,
730                 getter,
731                 val,
732                 attrCfg;
734             if (name.indexOf(DOT) !== -1) {
735                 path = name.split(DOT);
736                 name = path.shift();
737             }
739             // On Demand - Should be rare - handles out of
740             // order valueFn, setter, getter references
741             if (tCfgs && tCfgs[name]) {
742                 this._addOutOfOrder(name, tCfgs[name]);
743             }
745             attrCfg = this._state.data[name] || {};
747             // Lazy Init
748             if (attrCfg.lazy) {
749                 attrCfg = attrCfg.lazy;
750                 this._addLazyAttr(name, attrCfg);
751             }
753             val = this._getStateVal(name, attrCfg);
755             getter = attrCfg.getter;
757             if (getter && !getter.call) {
758                 getter = this[getter];
759             }
761             val = (getter) ? getter.call(this, val, fullName) : val;
762             val = (path) ? O.getValue(val, path) : val;
764             return val;
765         },
767         /**
768          * Gets the stored value for the attribute, from either the
769          * internal state object, or the state proxy if it exits
770          *
771          * @method _getStateVal
772          * @private
773          * @param {String} name The name of the attribute
774          * @param {Object} [cfg] Optional config hash for the attribute. This is added for performance along the critical path,
775          * where the calling method has already obtained the config from state.
776          *
777          * @return {Any} The stored value of the attribute
778          */
779         _getStateVal : function(name, cfg) {
780             var stateProxy = this._stateProxy;
782             if (!cfg) {
783                 cfg = this._state.getAll(name) || {};
784             }
786             return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value;
787         },
789         /**
790          * Sets the stored value for the attribute, in either the
791          * internal state object, or the state proxy if it exits
792          *
793          * @method _setStateVal
794          * @private
795          * @param {String} name The name of the attribute
796          * @param {Any} value The value of the attribute
797          */
798         _setStateVal : function(name, value) {
799             var stateProxy = this._stateProxy;
800             if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
801                 stateProxy[name] = value;
802             } else {
803                 this._state.add(name, VALUE, value);
804             }
805         },
807         /**
808          * Updates the stored value of the attribute in the privately held State object,
809          * if validation and setter passes.
810          *
811          * @method _setAttrVal
812          * @private
813          * @param {String} attrName The attribute name.
814          * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
815          * @param {Any} prevVal The currently stored value of the attribute.
816          * @param {Any} newVal The value which is going to be stored.
817          * @param {Object} [opts] Optional data providing the circumstances for the change.
818          * @param {Object} [attrCfg] Optional config hash for the attribute. This is added for performance along the critical path,
819          * where the calling method has already obtained the config from state.
820          *
821          * @return {booolean} true if the new attribute value was stored, false if not.
822          */
823         _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) {
825             var host = this,
826                 allowSet = true,
827                 cfg = attrCfg || this._state.data[attrName] || {},
828                 validator = cfg.validator,
829                 setter = cfg.setter,
830                 initializing = cfg.initializing,
831                 prevRawVal = this._getStateVal(attrName, cfg),
832                 name = subAttrName || attrName,
833                 retVal,
834                 valid;
836             if (validator) {
837                 if (!validator.call) {
838                     // Assume string - trying to keep critical path tight, so avoiding Lang check
839                     validator = this[validator];
840                 }
841                 if (validator) {
842                     valid = validator.call(host, newVal, name, opts);
844                     if (!valid && initializing) {
845                         newVal = cfg.defaultValue;
846                         valid = true; // Assume it's valid, for perf.
847                     }
848                 }
849             }
851             if (!validator || valid) {
852                 if (setter) {
853                     if (!setter.call) {
854                         // Assume string - trying to keep critical path tight, so avoiding Lang check
855                         setter = this[setter];
856                     }
857                     if (setter) {
858                         retVal = setter.call(host, newVal, name, opts);
860                         if (retVal === INVALID_VALUE) {
861                             if (initializing) {
862                                 newVal = cfg.defaultValue;
863                             } else {
864                                 allowSet = false;
865                             }
866                         } else if (retVal !== undefined){
867                             newVal = retVal;
868                         }
869                     }
870                 }
872                 if (allowSet) {
873                     if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) {
874                         allowSet = false;
875                     } else {
876                         // Store value
877                         if (!(INIT_VALUE in cfg)) {
878                             cfg.initValue = newVal;
879                         }
880                         host._setStateVal(attrName, newVal);
881                     }
882                 }
884             } else {
885                 allowSet = false;
886             }
888             return allowSet;
889         },
891         /**
892          * Sets multiple attribute values.
893          *
894          * @method setAttrs
895          * @param {Object} attrs  An object with attributes name/value pairs.
896          * @param {Object} [opts] Optional data providing the circumstances for the change.
897          * @return {Object} A reference to the host object.
898          * @chainable
899          */
900         setAttrs : function(attrs, opts) {
901             return this._setAttrs(attrs, opts);
902         },
904         /**
905          * Implementation behind the public setAttrs method, to set multiple attribute values.
906          *
907          * @method _setAttrs
908          * @protected
909          * @param {Object} attrs  An object with attributes name/value pairs.
910          * @param {Object} [opts] Optional data providing the circumstances for the change
911          * @return {Object} A reference to the host object.
912          * @chainable
913          */
914         _setAttrs : function(attrs, opts) {
915             var attr;
916             for (attr in attrs) {
917                 if ( attrs.hasOwnProperty(attr) ) {
918                     this.set(attr, attrs[attr], opts);
919                 }
920             }
921             return this;
922         },
924         /**
925          * Gets multiple attribute values.
926          *
927          * @method getAttrs
928          * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
929          * returned. If set to true, all attributes modified from their initial values are returned.
930          * @return {Object} An object with attribute name/value pairs.
931          */
932         getAttrs : function(attrs) {
933             return this._getAttrs(attrs);
934         },
936         /**
937          * Implementation behind the public getAttrs method, to get multiple attribute values.
938          *
939          * @method _getAttrs
940          * @protected
941          * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
942          * returned. If set to true, all attributes modified from their initial values are returned.
943          * @return {Object} An object with attribute name/value pairs.
944          */
945         _getAttrs : function(attrs) {
946             var obj = {},
947                 attr, i, len,
948                 modifiedOnly = (attrs === true);
950             // TODO - figure out how to get all "added"
951             if (!attrs || modifiedOnly) {
952                 attrs = O.keys(this._state.data);
953             }
955             for (i = 0, len = attrs.length; i < len; i++) {
956                 attr = attrs[i];
958                 if (!modifiedOnly || this._getStateVal(attr) != this._state.get(attr, INIT_VALUE)) {
959                     // Go through get, to honor cloning/normalization
960                     obj[attr] = this.get(attr);
961                 }
962             }
964             return obj;
965         },
967         /**
968          * Configures a group of attributes, and sets initial values.
969          *
970          * <p>
971          * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
972          * The caller is responsible for merging/cloning the configuration object if required.
973          * </p>
974          *
975          * @method addAttrs
976          * @chainable
977          *
978          * @param {Object} cfgs An object with attribute name/configuration pairs.
979          * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
980          * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
981          * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
982          * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
983          * See <a href="#method_addAttr">addAttr</a>.
984          *
985          * @return {Object} A reference to the host object.
986          */
987         addAttrs : function(cfgs, values, lazy) {
988             if (cfgs) {
989                 this._tCfgs = cfgs;
990                 this._tVals = (values) ? this._normAttrVals(values) : null;
991                 this._addAttrs(cfgs, this._tVals, lazy);
992                 this._tCfgs = this._tVals = null;
993             }
995             return this;
996         },
998         /**
999          * Implementation behind the public addAttrs method.
1000          *
1001          * This method is invoked directly by get if it encounters a scenario
1002          * in which an attribute's valueFn attempts to obtain the
1003          * value an attribute in the same group of attributes, which has not yet
1004          * been added (on demand initialization).
1005          *
1006          * @method _addAttrs
1007          * @private
1008          * @param {Object} cfgs An object with attribute name/configuration pairs.
1009          * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
1010          * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
1011          * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
1012          * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
1013          * See <a href="#method_addAttr">addAttr</a>.
1014          */
1015         _addAttrs : function(cfgs, values, lazy) {
1016             var tCfgs = this._tCfgs,
1017                 tVals = this._tVals,
1018                 attr,
1019                 attrCfg,
1020                 value;
1022             for (attr in cfgs) {
1023                 if (cfgs.hasOwnProperty(attr)) {
1025                     // Not Merging. Caller is responsible for isolating configs
1026                     attrCfg = cfgs[attr];
1027                     attrCfg.defaultValue = attrCfg.value;
1029                     // Handle simple, complex and user values, accounting for read-only
1030                     value = this._getAttrInitVal(attr, attrCfg, tVals);
1032                     if (value !== undefined) {
1033                         attrCfg.value = value;
1034                     }
1036                     if (tCfgs[attr]) {
1037                         tCfgs[attr] = undefined;
1038                     }
1040                     this.addAttr(attr, attrCfg, lazy);
1041                 }
1042             }
1043         },
1045         /**
1046          * Utility method to protect an attribute configuration
1047          * hash, by merging the entire object and the individual
1048          * attr config objects.
1049          *
1050          * @method _protectAttrs
1051          * @protected
1052          * @param {Object} attrs A hash of attribute to configuration object pairs.
1053          * @return {Object} A protected version of the attrs argument.
1054          * @deprecated Use `AttributeCore.protectAttrs()` or
1055          *   `Attribute.protectAttrs()` which are the same static utility method.
1056          */
1057         _protectAttrs : AttributeCore.protectAttrs,
1059         /**
1060          * Utility method to normalize attribute values. The base implementation
1061          * simply merges the hash to protect the original.
1062          *
1063          * @method _normAttrVals
1064          * @param {Object} valueHash An object with attribute name/value pairs
1065          *
1066          * @return {Object} An object literal with 2 properties - "simple" and "complex",
1067          * containing simple and complex attribute values respectively keyed
1068          * by the top level attribute name, or null, if valueHash is falsey.
1069          *
1070          * @private
1071          */
1072         _normAttrVals : function(valueHash) {
1073             var vals,
1074                 subvals,
1075                 path,
1076                 attr,
1077                 v, k;
1079             if (!valueHash) {
1080                 return null;
1081             }
1083             vals = {};
1085             for (k in valueHash) {
1086                 if (valueHash.hasOwnProperty(k)) {
1087                     if (k.indexOf(DOT) !== -1) {
1088                         path = k.split(DOT);
1089                         attr = path.shift();
1091                         subvals = subvals || {};
1093                         v = subvals[attr] = subvals[attr] || [];
1094                         v[v.length] = {
1095                             path : path,
1096                             value: valueHash[k]
1097                         };
1098                     } else {
1099                         vals[k] = valueHash[k];
1100                     }
1101                 }
1102             }
1104             return { simple:vals, complex:subvals };
1105         },
1107         /**
1108          * Returns the initial value of the given attribute from
1109          * either the default configuration provided, or the
1110          * over-ridden value if it exists in the set of initValues
1111          * provided and the attribute is not read-only.
1112          *
1113          * @param {String} attr The name of the attribute
1114          * @param {Object} cfg The attribute configuration object
1115          * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
1116          *
1117          * @return {Any} The initial value of the attribute.
1118          *
1119          * @method _getAttrInitVal
1120          * @private
1121          */
1122         _getAttrInitVal : function(attr, cfg, initValues) {
1123             var val = cfg.value,
1124                 valFn = cfg.valueFn,
1125                 tmpVal,
1126                 initValSet = false,
1127                 readOnly = cfg.readOnly,
1128                 simple,
1129                 complex,
1130                 i,
1131                 l,
1132                 path,
1133                 subval,
1134                 subvals;
1136             if (!readOnly && initValues) {
1137                 // Simple Attributes
1138                 simple = initValues.simple;
1139                 if (simple && simple.hasOwnProperty(attr)) {
1140                     val = simple[attr];
1141                     initValSet = true;
1142                 }
1143             }
1145             if (valFn && !initValSet) {
1146                 if (!valFn.call) {
1147                     valFn = this[valFn];
1148                 }
1149                 if (valFn) {
1150                     tmpVal = valFn.call(this, attr);
1151                     val = tmpVal;
1152                 }
1153             }
1155             if (!readOnly && initValues) {
1157                 // Complex Attributes (complex values applied, after simple, in case both are set)
1158                 complex = initValues.complex;
1160                 if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) {
1161                     subvals = complex[attr];
1162                     for (i = 0, l = subvals.length; i < l; ++i) {
1163                         path = subvals[i].path;
1164                         subval = subvals[i].value;
1165                         O.setValue(val, path, subval);
1166                     }
1167                 }
1168             }
1170             return val;
1171         },
1173         /**
1174          * Utility method to set up initial attributes defined during construction,
1175          * either through the constructor.ATTRS property, or explicitly passed in.
1176          *
1177          * @method _initAttrs
1178          * @protected
1179          * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>).
1180          *        These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor.
1181          * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>).
1182          *        These are not merged/cloned. The caller is responsible for isolating user provided values if required.
1183          * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
1184          */
1185         _initAttrs : function(attrs, values, lazy) {
1186             // ATTRS support for Node, which is not Base based
1187             attrs = attrs || this.constructor.ATTRS;
1189             var Base = Y.Base,
1190                 BaseCore = Y.BaseCore,
1191                 baseInst = (Base && Y.instanceOf(this, Base)),
1192                 baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore));
1194             if (attrs && !baseInst && !baseCoreInst) {
1195                 this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy);
1196             }
1197         }
1198     };
1200     Y.AttributeCore = AttributeCore;
1203 }, '3.13.0', {"requires": ["oop"]});