NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / attribute-core / attribute-core-debug.js
bloba9f709218aec36b6d8cdcd1e461b29e523bffb4d
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) {
413             Y.log('Adding attribute: ' + name, 'info', 'attribute');
415             var host = this, // help compression
416                 state = host._state,
417                 data = state.data,
418                 value,
419                 added,
420                 hasValue;
422             config = config || {};
424             if (LAZY_ADD in config) {
425                 lazy = config[LAZY_ADD];
426             }
428             added = state.get(name, ADDED);
430             if (lazy && !added) {
431                 state.data[name] = {
432                     lazy : config,
433                     added : true
434                 };
435             } else {
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');}
445                     if (hasValue) {
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;
456                     }
458                     config.added = true;
459                     config.initializing = true;
461                     data[name] = config;
463                     if (hasValue) {
464                         // Go through set, so that raw values get normalized/validated
465                         host.set(name, value);
466                     }
468                     config.initializing = false;
469                 }
470             }
472             return host;
473         },
475         /**
476          * Checks if the given attribute has been added to the host
477          *
478          * @method attrAdded
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.
482          */
483         attrAdded: function(name) {
484             return !!(this._state.get(name, ADDED));
485         },
487         /**
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.
491          *
492          * @method get
493          *
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>)
496          *
497          * @return {Any} The value of the attribute
498          */
499         get : function(name) {
500             return this._getAttr(name);
501         },
503         /**
504          * Checks whether or not the attribute is one which has been
505          * added lazily and still requires initialization.
506          *
507          * @method _isLazyAttr
508          * @private
509          * @param {String} name The name of the attribute
510          * @return {boolean} true if it's a lazily added attribute, false otherwise.
511          */
512         _isLazyAttr: function(name) {
513             return this._state.get(name, LAZY);
514         },
516         /**
517          * Finishes initializing an attribute which has been lazily added.
518          *
519          * @method _addLazyAttr
520          * @private
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.
524          */
525         _addLazyAttr: function(name, lazyCfg) {
526             var state = this._state;
528             lazyCfg = lazyCfg || state.get(name, LAZY);
530             if (lazyCfg) {
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
535                 // removed.
536                 state.data[name].lazy = undefined;
538                 lazyCfg.isLazyAdd = true;
540                 this.addAttr(name, lazyCfg);
541             }
542         },
544         /**
545          * Sets the value of an attribute.
546          *
547          * @method set
548          * @chainable
549          *
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.
556          */
557         set : function(name, val, opts) {
558             return this._setAttr(name, val, opts);
559         },
561         /**
562          * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
563          *
564          * @method _set
565          * @protected
566          * @chainable
567          *
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.
572          */
573         _set : function(name, val, opts) {
574             return this._setAttr(name, val, opts, true);
575         },
577         /**
578          * Provides the common implementation for the public set and protected _set methods.
579          *
580          * See <a href="#method_set">set</a> for argument details.
581          *
582          * @method _setAttr
583          * @protected
584          * @chainable
585          *
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.
591          *
592          * @return {Object} A reference to the host object.
593          */
594         _setAttr : function(name, val, opts, force)  {
595             var allowSet = true,
596                 state = this._state,
597                 stateProxy = this._stateProxy,
598                 tCfgs = this._tCfgs,
599                 cfg,
600                 initialSet,
601                 strPath,
602                 path,
603                 currVal,
604                 writeOnce,
605                 initializing;
607             if (name.indexOf(DOT) !== -1) {
608                 strPath = name;
610                 path = name.split(DOT);
611                 name = path.shift();
612             }
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]);
617             }
619             cfg = state.data[name] || {};
621             if (cfg.lazy) {
622                 cfg = cfg.lazy;
623                 this._addLazyAttr(name, cfg);
624             }
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?
630                 initialSet = false;
631             }
633             writeOnce = cfg.writeOnce;
634             initializing = cfg.initializing;
636             if (!initialSet && !force) {
638                 if (writeOnce) {
639                     Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute');
640                     allowSet = false;
641                 }
643                 if (cfg.readOnly) {
644                     Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute');
645                     allowSet = false;
646                 }
647             }
649             if (!initializing && !force && writeOnce === INIT_ONLY) {
650                 Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce: "initOnly"', 'warn', 'attribute');
651                 allowSet = false;
652             }
654             if (allowSet) {
655                 // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
656                 if (!initialSet) {
657                     currVal =  this.get(name);
658                 }
660                 if (path) {
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');
665                        allowSet = false;
666                    }
667                 }
669                 if (allowSet) {
670                     if (!this._fireAttrChange || initializing) {
671                         this._setAttrVal(name, strPath, currVal, val, opts, cfg);
672                     } else {
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);
676                     }
677                 }
678             }
680             return this;
681         },
683         /**
684          * Utility method used by get/set to add attributes
685          * encountered out of order when calling addAttrs().
686          *
687          * For example, if:
688          *
689          *     this.addAttrs({
690          *          foo: {
691          *              setter: function() {
692          *                 // make sure this bar is available when foo is added
693          *                 this.get("bar");
694          *              }
695          *          },
696          *          bar: {
697          *              value: ...
698          *          }
699          *     });
700          *
701          * @method _addOutOfOrder
702          * @private
703          * @param name {String} attribute name
704          * @param cfg {Object} attribute configuration
705          */
706         _addOutOfOrder : function(name, cfg) {
708             var attrs = {};
709             attrs[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
716             // improvement.
717             this._addAttrs(attrs, this._tVals);
718         },
720         /**
721          * Provides the common implementation for the public get method,
722          * allowing Attribute hosts to over-ride either method.
723          *
724          * See <a href="#method_get">get</a> for argument details.
725          *
726          * @method _getAttr
727          * @protected
728          * @chainable
729          *
730          * @param {String} name The name of the attribute.
731          * @return {Any} The value of the attribute.
732          */
733         _getAttr : function(name) {
734             var fullName = name,
735                 tCfgs = this._tCfgs,
736                 path,
737                 getter,
738                 val,
739                 attrCfg;
741             if (name.indexOf(DOT) !== -1) {
742                 path = name.split(DOT);
743                 name = path.shift();
744             }
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]);
750             }
752             attrCfg = this._state.data[name] || {};
754             // Lazy Init
755             if (attrCfg.lazy) {
756                 attrCfg = attrCfg.lazy;
757                 this._addLazyAttr(name, attrCfg);
758             }
760             val = this._getStateVal(name, attrCfg);
762             getter = attrCfg.getter;
764             if (getter && !getter.call) {
765                 getter = this[getter];
766             }
768             val = (getter) ? getter.call(this, val, fullName) : val;
769             val = (path) ? O.getValue(val, path) : val;
771             return val;
772         },
774         /**
775          * Gets the stored value for the attribute, from either the
776          * internal state object, or the state proxy if it exits
777          *
778          * @method _getStateVal
779          * @private
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.
783          *
784          * @return {Any} The stored value of the attribute
785          */
786         _getStateVal : function(name, cfg) {
787             var stateProxy = this._stateProxy;
789             if (!cfg) {
790                 cfg = this._state.getAll(name) || {};
791             }
793             return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value;
794         },
796         /**
797          * Sets the stored value for the attribute, in either the
798          * internal state object, or the state proxy if it exits
799          *
800          * @method _setStateVal
801          * @private
802          * @param {String} name The name of the attribute
803          * @param {Any} value The value of the attribute
804          */
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;
809             } else {
810                 this._state.add(name, VALUE, value);
811             }
812         },
814         /**
815          * Updates the stored value of the attribute in the privately held State object,
816          * if validation and setter passes.
817          *
818          * @method _setAttrVal
819          * @private
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.
827          *
828          * @return {booolean} true if the new attribute value was stored, false if not.
829          */
830         _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) {
832             var host = this,
833                 allowSet = true,
834                 cfg = attrCfg || this._state.data[attrName] || {},
835                 validator = cfg.validator,
836                 setter = cfg.setter,
837                 initializing = cfg.initializing,
838                 prevRawVal = this._getStateVal(attrName, cfg),
839                 name = subAttrName || attrName,
840                 retVal,
841                 valid;
843             if (validator) {
844                 if (!validator.call) {
845                     // Assume string - trying to keep critical path tight, so avoiding Lang check
846                     validator = this[validator];
847                 }
848                 if (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.
854                     }
855                 }
856             }
858             if (!validator || valid) {
859                 if (setter) {
860                     if (!setter.call) {
861                         // Assume string - trying to keep critical path tight, so avoiding Lang check
862                         setter = this[setter];
863                     }
864                     if (setter) {
865                         retVal = setter.call(host, newVal, name, opts);
867                         if (retVal === INVALID_VALUE) {
868                             if (initializing) {
869                                 Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal + ', initializing to default value', 'warn', 'attribute');
870                                 newVal = cfg.defaultValue;
871                             } else {
872                                 Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal, 'warn', 'attribute');
873                                 allowSet = false;
874                             }
875                         } else if (retVal !== undefined){
876                             Y.log('Attribute: ' + attrName + ', raw value: ' + newVal + ' modified by setter to:' + retVal, 'info', 'attribute');
877                             newVal = retVal;
878                         }
879                     }
880                 }
882                 if (allowSet) {
883                     if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) {
884                         Y.log('Attribute: ' + attrName + ', value unchanged:' + newVal, 'warn', 'attribute');
885                         allowSet = false;
886                     } else {
887                         // Store value
888                         if (!(INIT_VALUE in cfg)) {
889                             cfg.initValue = newVal;
890                         }
891                         host._setStateVal(attrName, newVal);
892                     }
893                 }
895             } else {
896                 Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute');
897                 allowSet = false;
898             }
900             return allowSet;
901         },
903         /**
904          * Sets multiple attribute values.
905          *
906          * @method setAttrs
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.
910          * @chainable
911          */
912         setAttrs : function(attrs, opts) {
913             return this._setAttrs(attrs, opts);
914         },
916         /**
917          * Implementation behind the public setAttrs method, to set multiple attribute values.
918          *
919          * @method _setAttrs
920          * @protected
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.
924          * @chainable
925          */
926         _setAttrs : function(attrs, opts) {
927             var attr;
928             for (attr in attrs) {
929                 if ( attrs.hasOwnProperty(attr) ) {
930                     this.set(attr, attrs[attr], opts);
931                 }
932             }
933             return this;
934         },
936         /**
937          * Gets multiple attribute values.
938          *
939          * @method getAttrs
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.
943          */
944         getAttrs : function(attrs) {
945             return this._getAttrs(attrs);
946         },
948         /**
949          * Implementation behind the public getAttrs method, to get multiple attribute values.
950          *
951          * @method _getAttrs
952          * @protected
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.
956          */
957         _getAttrs : function(attrs) {
958             var obj = {},
959                 attr, i, len,
960                 modifiedOnly = (attrs === true);
962             // TODO - figure out how to get all "added"
963             if (!attrs || modifiedOnly) {
964                 attrs = O.keys(this._state.data);
965             }
967             for (i = 0, len = attrs.length; i < len; i++) {
968                 attr = attrs[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);
973                 }
974             }
976             return obj;
977         },
979         /**
980          * Configures a group of attributes, and sets initial values.
981          *
982          * <p>
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.
985          * </p>
986          *
987          * @method addAttrs
988          * @chainable
989          *
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>.
996          *
997          * @return {Object} A reference to the host object.
998          */
999         addAttrs : function(cfgs, values, lazy) {
1000             if (cfgs) {
1001                 this._tCfgs = cfgs;
1002                 this._tVals = (values) ? this._normAttrVals(values) : null;
1003                 this._addAttrs(cfgs, this._tVals, lazy);
1004                 this._tCfgs = this._tVals = null;
1005             }
1007             return this;
1008         },
1010         /**
1011          * Implementation behind the public addAttrs method.
1012          *
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).
1017          *
1018          * @method _addAttrs
1019          * @private
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>.
1026          */
1027         _addAttrs : function(cfgs, values, lazy) {
1028             var tCfgs = this._tCfgs,
1029                 tVals = this._tVals,
1030                 attr,
1031                 attrCfg,
1032                 value;
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;
1046                     }
1048                     if (tCfgs[attr]) {
1049                         tCfgs[attr] = undefined;
1050                     }
1052                     this.addAttr(attr, attrCfg, lazy);
1053                 }
1054             }
1055         },
1057         /**
1058          * Utility method to protect an attribute configuration
1059          * hash, by merging the entire object and the individual
1060          * attr config objects.
1061          *
1062          * @method _protectAttrs
1063          * @protected
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.
1068          */
1069         _protectAttrs : AttributeCore.protectAttrs,
1071         /**
1072          * Utility method to normalize attribute values. The base implementation
1073          * simply merges the hash to protect the original.
1074          *
1075          * @method _normAttrVals
1076          * @param {Object} valueHash An object with attribute name/value pairs
1077          *
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.
1081          *
1082          * @private
1083          */
1084         _normAttrVals : function(valueHash) {
1085             var vals,
1086                 subvals,
1087                 path,
1088                 attr,
1089                 v, k;
1091             if (!valueHash) {
1092                 return null;
1093             }
1095             vals = {};
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] || [];
1106                         v[v.length] = {
1107                             path : path,
1108                             value: valueHash[k]
1109                         };
1110                     } else {
1111                         vals[k] = valueHash[k];
1112                     }
1113                 }
1114             }
1116             return { simple:vals, complex:subvals };
1117         },
1119         /**
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.
1124          *
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
1128          *
1129          * @return {Any} The initial value of the attribute.
1130          *
1131          * @method _getAttrInitVal
1132          * @private
1133          */
1134         _getAttrInitVal : function(attr, cfg, initValues) {
1135             var val = cfg.value,
1136                 valFn = cfg.valueFn,
1137                 tmpVal,
1138                 initValSet = false,
1139                 readOnly = cfg.readOnly,
1140                 simple,
1141                 complex,
1142                 i,
1143                 l,
1144                 path,
1145                 subval,
1146                 subvals;
1148             if (!readOnly && initValues) {
1149                 // Simple Attributes
1150                 simple = initValues.simple;
1151                 if (simple && simple.hasOwnProperty(attr)) {
1152                     val = simple[attr];
1153                     initValSet = true;
1154                 }
1155             }
1157             if (valFn && !initValSet) {
1158                 if (!valFn.call) {
1159                     valFn = this[valFn];
1160                 }
1161                 if (valFn) {
1162                     tmpVal = valFn.call(this, attr);
1163                     val = tmpVal;
1164                 }
1165             }
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);
1178                     }
1179                 }
1180             }
1182             return val;
1183         },
1185         /**
1186          * Utility method to set up initial attributes defined during construction,
1187          * either through the constructor.ATTRS property, or explicitly passed in.
1188          *
1189          * @method _initAttrs
1190          * @protected
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>).
1196          */
1197         _initAttrs : function(attrs, values, lazy) {
1198             // ATTRS support for Node, which is not Base based
1199             attrs = attrs || this.constructor.ATTRS;
1201             var Base = Y.Base,
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);
1208             }
1209         }
1210     };
1212     Y.AttributeCore = AttributeCore;
1215 }, '3.13.0', {"requires": ["oop"]});