~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/attribute/attribute.js

  • Committer: Matt Nordhoff
  • Date: 2010-02-26 04:37:13 UTC
  • mfrom: (400 trunk)
  • mto: This revision was merged to the branch mainline in revision 401.
  • Revision ID: mnordhoff@mattnordhoff.com-20100226043713-7mw3r6dr9qowutmi
Merge trunk for NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0pr2
 
6
*/
 
7
YUI.add('attribute', function(Y) {
 
8
 
 
9
    /**
 
10
     * Managed Attribute Provider
 
11
     * @module attribute
 
12
     */
 
13
 
 
14
    /**
 
15
     * Maintain state for a collection of items.  Individual properties 
 
16
     * are stored in hash tables.  This is instead of having state objects 
 
17
     * for each item in the collection.  For large collections, especially 
 
18
     * changing ones, this approach may perform better.
 
19
     * 
 
20
     * @constructor
 
21
     * @class State
 
22
     */
 
23
    Y.State = function() { 
 
24
 
 
25
        /**
 
26
         * Hash of attributes
 
27
         * @property data
 
28
         */
 
29
        this.data = {};
 
30
    };
 
31
 
 
32
    Y.State.prototype = {
 
33
 
 
34
        /**
 
35
         * Add an item with all of the properties in the supplied object.
 
36
         * @method add
 
37
         * @param name {string} identifier for this attribute
 
38
         * @param o hash of attributes
 
39
         */
 
40
        add: function(name, o) {
 
41
            Y.each(o, function(v, k) {
 
42
                if (!this.data[k]) {
 
43
                    this.data[k] = {};
 
44
                }
 
45
 
 
46
                this.data[k][name] = v;
 
47
            }, this);
 
48
        },
 
49
 
 
50
        /**
 
51
         * Remove entire item, or optionally specified fields
 
52
         * @method remove
 
53
         * @param name {string} name of attribute
 
54
         * @param o {string|object|array} single key or collection of keys to delete
 
55
         */
 
56
        remove: function(name, o) {
 
57
            var d = this.data, 
 
58
                del = function(key) {
 
59
                    if (d[key] && (name in d[key])) {
 
60
                        delete d[key][name];
 
61
                    }
 
62
                };
 
63
 
 
64
            if (Y.Lang.isString(o)) {
 
65
                del(o);
 
66
            } else {
 
67
                Y.each(o || d, function(v, k) {
 
68
                    if(Y.Lang.isString(k)) {
 
69
                        del(k);
 
70
                    } else {
 
71
                        del(v);
 
72
                    }
 
73
                }, this);
 
74
 
 
75
            }
 
76
        },
 
77
 
 
78
        /**
 
79
         * For a given item, gets an attribute.  If key is not
 
80
         * supplied, a disposable object with all attributes is 
 
81
         * returned.  Use of the latter option makes sense when
 
82
         * working with single items, but not if object explosion
 
83
         * might cause gc problems.
 
84
         * @method get
 
85
         * @param name {string} name of attribute
 
86
         * @param key {string} optional attribute to get
 
87
         * @return either the value of the supplied key or an object with
 
88
         * all data.
 
89
         */
 
90
        // get: function(name, key, val) {
 
91
        get: function(name, key) {
 
92
            var d = this.data,
 
93
                o;
 
94
 
 
95
            if (key) {
 
96
                return (d[key] && name in d[key]) ?  d[key][name] : undefined;
 
97
            } else {
 
98
                Y.each(d, function(v, k) {
 
99
                    if (name in d[k]) {
 
100
                        o = o || {};
 
101
                        o[k] = v[name];
 
102
                    }
 
103
                }, this);
 
104
 
 
105
                return o;
 
106
 
 
107
            }
 
108
        }
 
109
 
 
110
        // figure out what kind of functionality we may need here
 
111
        // get whole list
 
112
        // get a list of items and values for a given key
 
113
        // get a list of items where a key has the supplied value
 
114
        /*
 
115
        list: function(key, val) {
 
116
            var o = {}, d = this.data, test = !L.isUndefined(val);
 
117
 
 
118
            Y.each(this, function(v, k) {
 
119
 
 
120
                // verify key
 
121
                if (key && k !== key) {
 
122
                    return;
 
123
                // verify value.  note, v will be the item names, so this
 
124
                // isn't working ... need to iterate v items
 
125
                } else if (test && v !== val) {
 
126
                    return;
 
127
                }
 
128
 
 
129
                o[k] = v;
 
130
 
 
131
            }, this);
 
132
 
 
133
            return o;
 
134
        }
 
135
        */
 
136
    };
 
137
 
 
138
    /**
 
139
     * Managed Attribute Provider
 
140
     * @module attribute
 
141
     */
 
142
 
 
143
    var O = Y.Object,
 
144
 
 
145
        DOT = ".",
 
146
        CHANGE = "Change",
 
147
        GET = "get",
 
148
        SET = "set",
 
149
        VALUE = "value",
 
150
        CLONE = "clone",
 
151
        READ_ONLY = "readOnly",
 
152
        WRITE_ONCE = "writeOnce",
 
153
        VALIDATOR = "validator",
 
154
 
 
155
        CLONE_ENUM;
 
156
 
 
157
    /**
 
158
     * <p>
 
159
     * Attribute provides managed attribute support. 
 
160
     * </p>
 
161
     * <p>
 
162
     * The class is designed to be augmented onto a host class, 
 
163
     * and allows the host to support get/set methods for attributes,
 
164
     * initial configuration support and attribute change events.
 
165
     * </p>
 
166
     * <p>Attributes added to the host can:</p>
 
167
     * <ul>
 
168
     *     <li>Be defined as read-only.</li>
 
169
     *     <li>Be defined as write-once.</li>
 
170
     *     <li>Be defined with a set function, used to manipulate
 
171
     *     values passed to Attribute's set method, before they are stored.</li>
 
172
     *     <li>Be defined with a validator function, to validate values before they are stored.</li>
 
173
     *     <li>Be defined with a get function, which can be used to manipulate stored values,
 
174
     *     before they are returned by Attribute's get method.</li>
 
175
     *     <li>Specify if and how they should be cloned on 'get' (see <a href="#property_CLONE">Attribute.CLONE</a> for supported clone modes).</li>
 
176
     * </ul>
 
177
     *
 
178
     * <p>See the <a href="#method_addAtt">addAtt</a> method, for details about how to add attributes with
 
179
     * a specific configuration</p>
 
180
     *
 
181
     * @class Attribute
 
182
     * @uses Event.Target
 
183
     */
 
184
    function Attribute() {
 
185
        Y.Event.Target.call(this, {emitFacade:true});
 
186
        this._conf = this._conf || new Y.State();
 
187
    }
 
188
 
 
189
    /**
 
190
     * <p>
 
191
     * Constants for clone formats supported by Attribute.
 
192
     * </p>
 
193
     * <p>
 
194
     * By default attribute values returned by the get method
 
195
     * are not cloned. However setting the attribute's "clone"
 
196
     * property to:
 
197
     * </p>
 
198
     * <dl>
 
199
     *     <dt>Attribute.CLONE.DEEP</dt>
 
200
     *     <dd>Will result in a deep cloned value being returned
 
201
     *        (using YUI's clone method). This can be expensive for complex
 
202
     *        objects.
 
203
     *     </dd>
 
204
     *     <dt>Attribute.CLONE.SHALLOW</dt>
 
205
     *     <dd>Will result in a shallow cloned value being returned
 
206
     *        (using YUI's merge method).
 
207
     *     </dd>
 
208
     *     <dt>Attribute.CLONE.IMMUTABLE</dt>
 
209
     *     <dd>Will result in a deep cloned value being returned
 
210
     *         when using the get method. Additionally users will
 
211
     *         not be able to set sub values of the attribute 
 
212
     *         using the complex attribute notation (obj.set("x.y.z, 5)).
 
213
     *         However the value of the attribute can be changed, making
 
214
     *         it different from a READONLY attribute.
 
215
     *     </dd>
 
216
     *     <dt>Attribute.CLONE.NONE</dt>
 
217
     *     <dd>
 
218
     *         The value will not be cloned, resulting in a reference
 
219
     *         to the stored value being passed back, if the value is an object.
 
220
     *         This is the default behavior.
 
221
     *     </dd>
 
222
     * </dl>
 
223
     * 
 
224
     * @property CLONE
 
225
     * @static
 
226
     * @final
 
227
     * @type Object
 
228
     */
 
229
    Attribute.CLONE = {
 
230
        NONE : 0,
 
231
        DEEP : 1,
 
232
        SHALLOW : 2,
 
233
        IMMUTABLE: 3
 
234
    };
 
235
 
 
236
    CLONE_ENUM = Attribute.CLONE;
 
237
 
 
238
    Attribute.prototype = {
 
239
        /**
 
240
         * <p>
 
241
         * Adds an attribute, with the provided configuration to the host object. Intended
 
242
         * to be used by the host object to setup it's set of available attributes.
 
243
         * </p>
 
244
         * <p>
 
245
         * The config argument object literal supports the following optional properties:
 
246
         * </p>
 
247
         * <dl>
 
248
         *    <dt>value &#60;Any&#62;</dt>
 
249
         *    <dd>The initial value to set on the attribute</dd>
 
250
         *    <dt>readOnly &#60;Boolean&#62;</dt>
 
251
         *    <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
 
252
         *        cannot be set by invoking the set method.</dd>
 
253
         *    <dt>writeOnce &#60;Boolean&#62;</dt>
 
254
         *    <dd>Whether or not the attribute is "write once". Attributes having writeOnce set to true, 
 
255
         *        can only have their values set once, be it through the default configuration, 
 
256
         *        constructor configuration arguments, or by invoking set.</dd>
 
257
         *    <dt>set &#60;Function&#62;</dt>
 
258
         *    <dd>The setter function to be invoked (within the context of the host object) before 
 
259
         *        the attribute is stored by a call to the set method. The value returned by the 
 
260
         *        set function will be the finally stored value.</dd>
 
261
         *    <dt>get &#60;Function&#62;</dt>
 
262
         *    <dd>The getter function to be invoked (within the context of the host object) before
 
263
         *    the stored values is returned to a user invoking the get method for the attribute.
 
264
         *    The value returned by the get function is the final value which will be returned to the 
 
265
         *    user when they invoke get.</dd>
 
266
         *    <dt>validator &#60;Function&#62;</dt>
 
267
         *    <dd>The validator function which is invoked prior to setting the stored value. Returning
 
268
         *    false from the validator function will prevent the value from being stored</dd>
 
269
         *    <dt>clone &#60;int&#62;</dt>
 
270
         *    <dd>If and how the value returned by a call to the get method, should be de-referenced from
 
271
         *    the stored value. By default values are not cloned, and hence a call to get will return
 
272
         *    a reference to the stored value. See Attribute.CLONE for more details about the clone 
 
273
         *    options available</dd>
 
274
         * </dl>
 
275
         *
 
276
         * @method addAtt
 
277
         * 
 
278
         * @param {String} name The attribute key
 
279
         * @param {Object} config (optional) An object literal specifying the configuration for the attribute.
 
280
         * <strong>NOTE:</strong> The config object is modified when adding an attribute, 
 
281
         * so if you need to protect the original values, you will need to merge or clone the object.
 
282
         * 
 
283
         */
 
284
        addAtt: function(name, config) {
 
285
            var value, hasValue = (VALUE in config);
 
286
 
 
287
 
 
288
            if(hasValue) {
 
289
                value = config.value;
 
290
                delete config.value;
 
291
            }
 
292
 
 
293
            config.initValue = value;
 
294
            this._conf.add(name, config);
 
295
 
 
296
            if (hasValue) {
 
297
                this.set(name, value);
 
298
            }
 
299
        },
 
300
 
 
301
        /**
 
302
         * Resets the given attribute or all attributes to the initial value.
 
303
         *
 
304
         * @method reset
 
305
         * @param {String} name optional An attribute to reset.  If omitted, all attributes are reset
 
306
         */
 
307
        reset: function(name) {
 
308
            if (name) {
 
309
                this.set(name, this._conf.data['initValue'][name]);
 
310
            } else {
 
311
                var initVals = this._conf.data['initValue'];
 
312
                Y.each(initVals, function(v, n) {
 
313
                    this._set(n, v);
 
314
                }, this);
 
315
            }
 
316
        },
 
317
 
 
318
        /**
 
319
         * Removes an attribute.
 
320
         *
 
321
         * @method removeAtt
 
322
         * @param {String} name The attribute key
 
323
         */
 
324
        removeAtt: function(name) {
 
325
            this._conf.remove(name);
 
326
        },
 
327
 
 
328
        /**
 
329
         * Returns the current value of the attribute. If the attribute
 
330
         * has been configured with a 'get' handler, this method will delegate
 
331
         * to the 'get' handler to obtain the value of the attribute.
 
332
         * The 'get' handler will be passed the current value of the attribute 
 
333
         * as the only argument.
 
334
         *
 
335
         * @method get
 
336
         *
 
337
         * @param {String} key The attribute whose value will be returned. If
 
338
         * the value of the attribute is an Object, dot notation can be used to
 
339
         * obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
 
340
         * 
 
341
         * @return {Any} The current value of the attribute
 
342
         */
 
343
        get: function(name) {
 
344
 
 
345
            var conf = this._conf,
 
346
                path,
 
347
                getFn,
 
348
                clone,
 
349
                val;
 
350
 
 
351
            if (name.indexOf(DOT) !== -1) {
 
352
                path = name.split(DOT);
 
353
                name = path.shift();
 
354
            }
 
355
 
 
356
            val = conf.get(name, VALUE);
 
357
            getFn = conf.get(name, GET);
 
358
            clone = conf.get(name, CLONE);
 
359
 
 
360
            val = (clone) ? this._cloneAttVal(val, clone) : val;
 
361
            val = (getFn) ? getFn.call(this, val) : val;
 
362
            val = (path) ? this._getSubAttVal(path, val) : val;
 
363
 
 
364
            return val;
 
365
        },
 
366
 
 
367
        /**
 
368
         * Allows setting of readOnly/writeOnce attributes.
 
369
         *
 
370
         * @method _set
 
371
         * @protected
 
372
         * @chainable
 
373
         * @return {Object} Reference to the host object
 
374
         */
 
375
        _set: function(name, val, opts) {
 
376
            return this.set(name, val, opts, true);
 
377
        },
 
378
 
 
379
        /**
 
380
         * Sets the value of an attribute.
 
381
         *
 
382
         * @method set
 
383
         * @chainable
 
384
         * 
 
385
         * @param {String} name The name of the attribute. Note, if the 
 
386
         * value of the attribute is an Object, dot notation can be used
 
387
         * to set the value of a property within the object 
 
388
         * (e.g. <code>set("x.y.z", 5)</code>), if the attribute has not
 
389
         * been declared as an immutable attribute (see <a href="#property_CLONE">Attribute.CLONE</a>).
 
390
         * 
 
391
         * @param {Any} value The value to apply to the attribute
 
392
         * 
 
393
         * @param {Object} opts Optional event data. This object will be mixed into
 
394
         * the event facade passed as the first argument to subscribers 
 
395
         * of attribute change events
 
396
         * 
 
397
         * @return {Object} Reference to the host object
 
398
         */
 
399
        set: function(name, val, opts, privateSet) {
 
400
            var conf = this._conf,
 
401
                data = conf.data,
 
402
                strPath,
 
403
                path,
 
404
                currVal,
 
405
                initialSet = (!data.value || !(name in data.value));
 
406
 
 
407
            if (name.indexOf(DOT) !== -1) {
 
408
                strPath = name;
 
409
                path = name.split(DOT);
 
410
                name = path.shift();
 
411
            }
 
412
 
 
413
            if (path && conf.get(name, CLONE) === CLONE_ENUM.IMMUTABLE) {
 
414
                return this;
 
415
            }
 
416
 
 
417
            if (!initialSet && !privateSet) {
 
418
                if (conf.get(name, WRITE_ONCE)) {
 
419
                    return this;
 
420
                }
 
421
                if (conf.get(name, READ_ONLY)) {
 
422
                    return this;
 
423
                }
 
424
            }
 
425
 
 
426
            if (!conf.get(name)) {
 
427
                return this;
 
428
            }
 
429
 
 
430
            currVal = this.get(name);
 
431
 
 
432
            if (path) {
 
433
               val = this._setSubAttVal(path, Y.clone(currVal), val);
 
434
               if (val === undefined) {
 
435
                   // Path not valid, don't set anything.
 
436
                   return this;
 
437
               }
 
438
            }
 
439
 
 
440
            this._fireAttChange(name, currVal, val, name, strPath, opts);
 
441
 
 
442
            return this;
 
443
        },
 
444
 
 
445
        /**
 
446
         * <p>
 
447
         * Alias for the Event.Target <a href="Event.Target.html#method_subscribe">subscribe</a> method.
 
448
         * </p>
 
449
         * 
 
450
         * <p>Subscribers using this method to listen for attribute change events will be notified just
 
451
         * <strong>before</strong> the state of the attribute has been modified, and before the default handler has been
 
452
         * invoked.</p>
 
453
         * 
 
454
         * <p>The <a href="Event.Target.html#method_after">after</a> method, inherited from Event Target, can be used by subscribers
 
455
         * who wish to be notified <strong>after</strong> the attribute's value has changed.</p>
 
456
         * 
 
457
         * @param {String} type The event type. For attribute change events, the event type is "[Attribute Name]Change", e.g.
 
458
         * for the attribute "enabled", the event type will be "enabledChange".
 
459
         * @param {Function} fn The subscribed function to invoke
 
460
         * @param {Object} context Optional execution context
 
461
         * @param {Any*} args* 0..n additional arguments to append to supply to the subscribed function when the event fires.
 
462
         * @method on
 
463
         * @return {Event.Handle} The handle object for unsubscribing the subscriber from the event.
 
464
         */
 
465
        on : function() {
 
466
            return this.subscribe.apply(this, arguments);
 
467
        },
 
468
 
 
469
        /**
 
470
         * Default handler implementation for set events
 
471
         *
 
472
         * @private
 
473
         * @method _defAttSet
 
474
         * @param {Event.Facade} e The event object for the custom event
 
475
         */
 
476
        _defAttSet : function(e) {
 
477
            var conf = this._conf,
 
478
                name = e.attrName,
 
479
                val = e.newVal,
 
480
                retVal,
 
481
                valFn  = conf.get(name, VALIDATOR),
 
482
                setFn = conf.get(name, SET);
 
483
 
 
484
            if (setFn) {
 
485
                retVal = setFn.call(this, val);
 
486
                if (retVal !== undefined) {
 
487
                    val = retVal; // setter can change value
 
488
                }
 
489
            }
 
490
 
 
491
            if (!valFn || valFn.call(this, val)) {
 
492
                conf.add(name, { value: val });
 
493
                e.newVal = conf.get(name, VALUE);
 
494
            } else {
 
495
                // Prevent "after" listeners from being 
 
496
                // invoked since nothing changed.
 
497
                e.stopImmediatePropagation();
 
498
            }
 
499
        },
 
500
 
 
501
        /**
 
502
         * Retrieves the sub value at the provided path,
 
503
         * from the value object provided.
 
504
         *
 
505
         * @method _getSubAttVal
 
506
         * @private
 
507
         *
 
508
         * @param {Array} path  A path array, specifying the object traversal path
 
509
         *                      from which to obtain the sub value.
 
510
         * @param {Object} val  The object from which to extract the property value
 
511
         * @return {Any} The value stored in the path or undefined if not found.
 
512
         */
 
513
        _getSubAttVal: function (path, val) {
 
514
            var pl = path.length,
 
515
                i;
 
516
 
 
517
            if (pl > 0) {
 
518
                for (i = 0; val !== undefined && i < pl; ++i) {
 
519
                    val = val[path[i]];
 
520
                }
 
521
            }
 
522
 
 
523
            return val;
 
524
        },
 
525
 
 
526
        /**
 
527
         * Sets the sub value at the provided path on the value object.
 
528
         * Returns the modified value object, or undefined if the path is invalid.
 
529
         *
 
530
         * @method _setSubAttVal
 
531
         * @private
 
532
         * 
 
533
         * @param {Array} path  A path array, specifying the object traversal path
 
534
         *                      at which to set the sub value.
 
535
         * @param {Object} val  The object on which to set the sub value.
 
536
         * @param {Any} subval  The sub value to set.
 
537
         * @return {Object}     The modified object, with the new sub value set, or 
 
538
         *                      undefined, if the path was invalid.
 
539
         */
 
540
        _setSubAttVal: function(path, val, subval) {
 
541
 
 
542
            var leafIdx = path.length-1,
 
543
                i,
 
544
                o;
 
545
 
 
546
            if (leafIdx >= 0) {
 
547
                o = val;
 
548
 
 
549
                for (i = 0; o !== undefined && i < leafIdx; ++i) {
 
550
                    o = o[path[i]];
 
551
                }
 
552
 
 
553
                // Not preventing new properties from being added
 
554
                if (o !== undefined /* && o[path[i]] !== undefined */) {
 
555
                    o[path[i]] = subval;
 
556
                } else {
 
557
                    val = undefined;
 
558
                }
 
559
            }
 
560
 
 
561
            return val;
 
562
        },
 
563
 
 
564
        /**
 
565
         * Sets multiple attribute values.
 
566
         * 
 
567
         * @method setAtts
 
568
         * @param {Object} atts  A hash of attributes: name/value pairs
 
569
         */
 
570
        setAtts: function(atts) {
 
571
            for (var att in atts) {
 
572
                if ( O.owns(atts, att) ) {
 
573
                    this.set(att, atts[att]);
 
574
                }
 
575
            }
 
576
        },
 
577
 
 
578
        /**
 
579
         * Gets multiple attribute values.
 
580
         *
 
581
         * @method getAtts
 
582
         * @param {Array} Optional. An array of attribute names, whose values are required. If omitted, all attribute values are
 
583
         * returned.
 
584
         * @return {Object} A hash of attributes: name/value pairs
 
585
         */
 
586
        getAtts: function(atts) {
 
587
            var o = {}, i, l, att;
 
588
            atts = atts || O.keys(this._conf.data[VALUE]);
 
589
 
 
590
            for (i = 0, l = atts.length; i < l; i++) {
 
591
                // Go through get, to retrieve massaged values and honor cloning
 
592
                att = atts[i];
 
593
                o[att] = this.get(att); 
 
594
            }
 
595
 
 
596
            return o;
 
597
        },
 
598
 
 
599
        /**
 
600
         * Configures attributes, and sets initial values
 
601
         *
 
602
         * @method _initAtts
 
603
         * @protected
 
604
         * 
 
605
         * @param {Object} cfg Attribute configuration object literal
 
606
         * @param {Object} initValues Name/value hash of initial values to apply
 
607
         */
 
608
        _initAtts : function(cfg, initValues) {
 
609
            if (cfg) {
 
610
                var att,
 
611
                    attCfg,
 
612
                    values,
 
613
                    value,
 
614
                    atts = cfg;
 
615
 
 
616
                values = this._splitAttVals(initValues);
 
617
 
 
618
                for (att in atts) {
 
619
                    if (O.owns(atts, att)) {
 
620
                        attCfg = Y.merge(atts[att]);
 
621
                        value = this._initAttVal(att, attCfg, values);
 
622
                        if (value !== undefined) {
 
623
                            attCfg.value = value;
 
624
                        }
 
625
 
 
626
                        this.addAtt(att, attCfg);
 
627
                    }
 
628
                }
 
629
            }
 
630
        },
 
631
 
 
632
        /**
 
633
         * Utility to split out regular attribute values
 
634
         * from complex attribute values, so that complex
 
635
         * attributes can be keyed by top level attribute name.
 
636
         *
 
637
         * @method _splitAttrValues
 
638
         * @param {Object} valueHash Name/value hash of initial values
 
639
         *
 
640
         * @return {Object} Object literal with 2 properties - "simple" and "complex",
 
641
         * containing simple and complex attribute values respectively keyed 
 
642
         * by attribute the top level attribute name.
 
643
         * @private
 
644
         */
 
645
        _splitAttVals: function(valueHash) {
 
646
            var vals = {},
 
647
                subvals = {},
 
648
                path,
 
649
                attr,
 
650
                v;
 
651
 
 
652
            for (var k in valueHash) {
 
653
                if (O.owns(valueHash, k)) {
 
654
                    if (k.indexOf(DOT) !== -1) {
 
655
                        path = k.split(DOT);
 
656
                        attr = path.shift();
 
657
                        v = subvals[attr] = subvals[attr] || [];
 
658
                        v[v.length] = {
 
659
                            path : path, 
 
660
                            value: valueHash[k]
 
661
                        };
 
662
                    } else {
 
663
                        vals[k] = valueHash[k];
 
664
                    }
 
665
                }
 
666
            }
 
667
            return { simple:vals, complex:subvals };
 
668
        },
 
669
 
 
670
        /**
 
671
         * Returns the initial value of the given attribute from
 
672
         * either the default configuration provided, or the 
 
673
         * over-ridden value if it exists in the initValues 
 
674
         * hash provided.
 
675
         *
 
676
         * @param {String} att Attribute name
 
677
         * @param {Object} cfg Default attribute configuration
 
678
         * object literal
 
679
         * @param {Object} initVales Initial attribute values, provided 
 
680
         * for the instance
 
681
         *
 
682
         * @return {Any} Initial value of the attribute.
 
683
         *
 
684
         * @method _initAttVal
 
685
         * @private
 
686
         */
 
687
        _initAttVal : function(att, cfg, initValues) {
 
688
 
 
689
            var hasVal = (VALUE in cfg),
 
690
                val = (cfg.valueFn) ? cfg.valueFn.call(this) : cfg.value,
 
691
                simple,
 
692
                complex,
 
693
                i,
 
694
                l,
 
695
                path,
 
696
                subval,
 
697
                subvals;
 
698
 
 
699
            if (!cfg[READ_ONLY] && initValues) {
 
700
                // Simple Attributes
 
701
                simple = initValues.simple;
 
702
                if (simple && O.owns(simple, att)) {
 
703
                    hasVal = true;
 
704
                    val = simple[att];
 
705
                }
 
706
 
 
707
                // Complex Attributes
 
708
                complex = initValues.complex;
 
709
                if (complex && O.owns(complex, att)) {
 
710
                    hasVal = true;
 
711
                    subvals = complex[att];
 
712
                    for (i = 0, l = subvals.length; i < l; ++i) {
 
713
                        path = subvals[i].path;
 
714
                        subval = subvals[i].value;
 
715
                        val = this._setSubAttVal(path, val, subval);
 
716
                    }
 
717
                }
 
718
            }
 
719
 
 
720
            return val;
 
721
        },
 
722
 
 
723
        /**
 
724
         * <p>
 
725
         * Clone utility method, which will 
 
726
         * clone the provided value using YUI's 
 
727
         * merge, or clone utilities based
 
728
         * on the clone type provided. See <a href="#property_CLONE">Attribute.CLONE</a>
 
729
         * </p>
 
730
         * 
 
731
         * @method _cloneAttVal
 
732
         * @private 
 
733
         * @param {Any} val Value to clone
 
734
         * @param {int} type Clone type to use, See the CLONE property
 
735
         * @return {Any} The cloned copy of the object, based on the provided type.
 
736
         */
 
737
        _cloneAttVal : function(val, type) {
 
738
            switch(type) {
 
739
                case CLONE_ENUM.SHALLOW:
 
740
                    val = Y.merge(val);
 
741
                    break;
 
742
                case CLONE_ENUM.DEEP:
 
743
                case CLONE_ENUM.IMMUTABLE:
 
744
                    val = Y.clone(val);
 
745
                    break;
 
746
            }
 
747
            return val;
 
748
        },
 
749
 
 
750
        /**
 
751
         * Utility method to help setup the event payload and 
 
752
         * fire the attribute change event.
 
753
         * 
 
754
         * @method _fireAttChange
 
755
         * @private
 
756
         * @param {String} type The event name
 
757
         * @param {Any} currVal The current value of the attribute
 
758
         * @param {Any} newVal The new value of the attribute
 
759
         * @param {String} attrName The name of the attribute
 
760
         * @param {String} strFullPath The full path of the property being changed, 
 
761
         * if this is a sub-attribute value being change
 
762
         * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
 
763
         */
 
764
        _fireAttChange: function(type, currVal, newVal, attrName, strFullPath, opts) {
 
765
            type = type + CHANGE;
 
766
 
 
767
            // TODO: Publishing temporarily, while we address event bubbling/queuing
 
768
            this.publish(type, {queuable:false, defaultFn:this._defAttSet, silent:true});
 
769
 
 
770
            var eData = {
 
771
                type: type,
 
772
                prevVal: currVal,
 
773
                newVal: newVal,
 
774
                attrName: attrName,
 
775
                subAttrName: strFullPath
 
776
            };
 
777
 
 
778
            if (opts) {
 
779
                Y.mix(eData, opts);
 
780
            }
 
781
 
 
782
            this.fire(eData);
 
783
        }
 
784
    };
 
785
 
 
786
    Y.mix(Attribute, Y.Event.Target, false, null, 1);
 
787
 
 
788
    Y.Attribute = Attribute;
 
789
 
 
790
 
 
791
 
 
792
}, '3.0.0pr2' ,{requires:['event']});