1YUI.add('attribute-core', function (Y, NAME) { 2 3 /** 4 * The State class maintains state for a collection of named items, with 5 * a varying number of properties defined. 6 * 7 * It avoids the need to create a separate class for the item, and separate instances 8 * of these classes for each item, by storing the state in a 2 level hash table, 9 * improving performance when the number of items is likely to be large. 10 * 11 * @constructor 12 * @class State 13 */ 14 Y.State = function() { 15 /** 16 * Hash of attributes 17 * @property data 18 */ 19 this.data = {}; 20 }; 21 22 Y.State.prototype = { 23 24 /** 25 * Adds a property to an item. 26 * 27 * @method add 28 * @param name {String} The name of the item. 29 * @param key {String} The name of the property. 30 * @param val {Any} The value of the property. 31 */ 32 add: function(name, key, val) { 33 var item = this.data[name]; 34 35 if (!item) { 36 item = this.data[name] = {}; 37 } 38 39 item[key] = val; 40 }, 41 42 /** 43 * Adds multiple properties to an item. 44 * 45 * @method addAll 46 * @param name {String} The name of the item. 47 * @param obj {Object} A hash of property/value pairs. 48 */ 49 addAll: function(name, obj) { 50 var item = this.data[name], 51 key; 52 53 if (!item) { 54 item = this.data[name] = {}; 55 } 56 57 for (key in obj) { 58 if (obj.hasOwnProperty(key)) { 59 item[key] = obj[key]; 60 } 61 } 62 }, 63 64 /** 65 * Removes a property from an item. 66 * 67 * @method remove 68 * @param name {String} The name of the item. 69 * @param key {String} The property to remove. 70 */ 71 remove: function(name, key) { 72 var item = this.data[name]; 73 74 if (item) { 75 delete item[key]; 76 } 77 }, 78 79 /** 80 * Removes multiple properties from an item, or removes the item completely. 81 * 82 * @method removeAll 83 * @param name {String} The name of the item. 84 * @param obj {Object|Array} Collection of properties to delete. If not provided, the entire item is removed. 85 */ 86 removeAll: function(name, obj) { 87 var data; 88 89 if (!obj) { 90 data = this.data; 91 92 if (name in data) { 93 delete data[name]; 94 } 95 } else { 96 Y.each(obj, function(value, key) { 97 this.remove(name, typeof key === 'string' ? key : value); 98 }, this); 99 } 100 }, 101 102 /** 103 * For a given item, returns the value of the property requested, or undefined if not found. 104 * 105 * @method get 106 * @param name {String} The name of the item 107 * @param key {String} Optional. The property value to retrieve. 108 * @return {Any} The value of the supplied property. 109 */ 110 get: function(name, key) { 111 var item = this.data[name]; 112 113 if (item) { 114 return item[key]; 115 } 116 }, 117 118 /** 119 * For the given item, returns an object with all of the 120 * item's property/value pairs. By default the object returned 121 * is a shallow copy of the stored data, but passing in true 122 * as the second parameter will return a reference to the stored 123 * data. 124 * 125 * @method getAll 126 * @param name {String} The name of the item 127 * @param reference {boolean} true, if you want a reference to the stored 128 * object 129 * @return {Object} An object with property/value pairs for the item. 130 */ 131 getAll : function(name, reference) { 132 var item = this.data[name], 133 key, obj; 134 135 if (reference) { 136 obj = item; 137 } else if (item) { 138 obj = {}; 139 140 for (key in item) { 141 if (item.hasOwnProperty(key)) { 142 obj[key] = item[key]; 143 } 144 } 145 } 146 147 return obj; 148 } 149 }; 150 /*For log lines*/ 151 /*jshint maxlen:200*/ 152 153 /** 154 * The attribute module provides an augmentable Attribute implementation, which 155 * adds configurable attributes and attribute change events to the class being 156 * augmented. It also provides a State class, which is used internally by Attribute, 157 * but can also be used independently to provide a name/property/value data structure to 158 * store state. 159 * 160 * @module attribute 161 */ 162 163 /** 164 * The attribute-core submodule provides the lightest level of attribute handling support 165 * without Attribute change events, or lesser used methods such as reset(), modifyAttrs(), 166 * and removeAttr(). 167 * 168 * @module attribute 169 * @submodule attribute-core 170 */ 171 var O = Y.Object, 172 Lang = Y.Lang, 173 174 DOT = ".", 175 176 // Externally configurable props 177 GETTER = "getter", 178 SETTER = "setter", 179 READ_ONLY = "readOnly", 180 WRITE_ONCE = "writeOnce", 181 INIT_ONLY = "initOnly", 182 VALIDATOR = "validator", 183 VALUE = "value", 184 VALUE_FN = "valueFn", 185 LAZY_ADD = "lazyAdd", 186 187 // Used for internal state management 188 ADDED = "added", 189 BYPASS_PROXY = "_bypassProxy", 190 INIT_VALUE = "initValue", 191 LAZY = "lazy", 192 193 INVALID_VALUE; 194 195 /** 196 * <p> 197 * AttributeCore provides the lightest level of configurable attribute support. It is designed to be 198 * augmented on to a host class, and provides the host with the ability to configure 199 * attributes to store and retrieve state, <strong>but without support for attribute change events</strong>. 200 * </p> 201 * <p>For example, attributes added to the host can be configured:</p> 202 * <ul> 203 * <li>As read only.</li> 204 * <li>As write once.</li> 205 * <li>With a setter function, which can be used to manipulate 206 * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li> 207 * <li>With a getter function, which can be used to manipulate stored values, 208 * before they are returned by Attribute's <a href="#method_get">get</a> method.</li> 209 * <li>With a validator function, to validate values before they are stored.</li> 210 * </ul> 211 * 212 * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration 213 * options available for attributes.</p> 214 * 215 * <p>Object/Classes based on AttributeCore can augment <a href="AttributeObservable.html">AttributeObservable</a>
216 * (with true for overwrite) and <a href="AttributeExtras.html">AttributeExtras</a> to add attribute event and 217 * additional, less commonly used attribute methods, such as `modifyAttr`, `removeAttr` and `reset`.</p> 218 * 219 * @class AttributeCore 220 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>). 221 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. 222 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>). 223 * These are not merged/cloned. The caller is responsible for isolating user provided values if required. 224 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>). 225 */ 226 function AttributeCore(attrs, values, lazy) { 227 // HACK: Fix #2531929 228 // 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. 229 // Too late in the release cycle to do anything about the core problem. 230 // 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). 231 this._yuievt = null; 232 233 this._initAttrHost(attrs, values, lazy); 234 } 235 236 /** 237 * <p>The value to return from an attribute setter in order to prevent the set from going through.</p> 238 * 239 * <p>You can return this value from your setter if you wish to combine validator and setter 240 * functionality into a single setter function, which either returns the massaged value to be stored or 241 * AttributeCore.INVALID_VALUE to prevent invalid values from being stored.</p> 242 * 243 * @property INVALID_VALUE 244 * @type Object 245 * @static 246 * @final 247 */ 248 AttributeCore.INVALID_VALUE = {}; 249 INVALID_VALUE = AttributeCore.INVALID_VALUE; 250 251 /** 252 * The list of properties which can be configured for 253 * each attribute (e.g. setter, getter, writeOnce etc.). 254 * 255 * This property is used internally as a whitelist for faster 256 * Y.mix operations. 257 * 258 * @property _ATTR_CFG 259 * @type Array 260 * @static 261 * @protected 262 */ 263 AttributeCore._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BYPASS_PROXY]; 264 265 /** 266 * Utility method to protect an attribute configuration hash, by merging the 267 * entire object and the individual attr config objects. 268 * 269 * @method protectAttrs 270 * @static 271 * @param {Object} attrs A hash of attribute to configuration object pairs. 272 * @return {Object} A protected version of the `attrs` argument. 273 */ 274 AttributeCore.protectAttrs = function (attrs) { 275 if (attrs) { 276 attrs = Y.merge(attrs); 277 for (var attr in attrs) { 278 if (attrs.hasOwnProperty(attr)) { 279 attrs[attr] = Y.merge(attrs[attr]); 280 } 281 } 282 } 283 284 return attrs; 285 }; 286 287 AttributeCore.prototype = { 288 289 /** 290 * Constructor logic for attributes. Initializes the host state, and sets up the inital attributes passed to the 291 * constructor. 292 * 293 * @method _initAttrHost 294 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>). 295 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. 296 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>). 297 * These are not merged/cloned. The caller is responsible for isolating user provided values if required. 298 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>). 299 * @private 300 */ 301 _initAttrHost : function(attrs, values, lazy) { 302 this._state = new Y.State(); 303 this._initAttrs(attrs, values, lazy); 304 }, 305 306 /** 307 * <p> 308 * Adds an attribute with the provided configuration to the host object. 309 * </p> 310 * <p> 311 * The config argument object supports the following properties: 312 * </p> 313 * 314 * <dl> 315 * <dt>value <Any></dt> 316 * <dd>The initial value to set on the attribute</dd> 317 * 318 * <dt>valueFn <Function | String></dt> 319 * <dd> 320 * <p>A function, which will return the initial value to set on the attribute. This is useful 321 * for cases where the attribute configuration is defined statically, but needs to 322 * reference the host instance ("this") to obtain an initial value. If both the value and valueFn properties are defined, 323 * the value returned by the valueFn has precedence over the value property, unless it returns undefined, in which 324 * case the value property is used.</p> 325 * 326 * <p>valueFn can also be set to a string, representing the name of the instance method to be used to retrieve the value.</p> 327 * </dd> 328 * 329 * <dt>readOnly <boolean></dt> 330 * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true 331 * cannot be modified by invoking the set method.</dd> 332 * 333 * <dt>writeOnce <boolean> or <string></dt> 334 * <dd> 335 * Whether or not the attribute is "write once". Attributes having writeOnce set to true, 336 * can only have their values set once, be it through the default configuration, 337 * constructor configuration arguments, or by invoking set. 338 * <p>The writeOnce attribute can also be set to the string "initOnly", 339 * in which case the attribute can only be set during initialization 340 * (when used with Base, this means it can only be set during construction)</p> 341 * </dd> 342 * 343 * <dt>setter <Function | String></dt> 344 * <dd> 345 * <p>The setter function used to massage or normalize the value passed to the set method for the attribute. 346 * The value returned by the setter will be the final stored value. Returning 347 * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent 348 * the value from being stored. 349 * </p> 350 * 351 * <p>setter can also be set to a string, representing the name of the instance method to be used as the setter function.</p> 352 * </dd> 353 * 354 * <dt>getter <Function | String></dt> 355 * <dd> 356 * <p> 357 * The getter function used to massage or normalize the value returned by the get method for the attribute. 358 * The value returned by the getter function is the value which will be returned to the user when they 359 * invoke get. 360 * </p> 361 * 362 * <p>getter can also be set to a string, representing the name of the instance method to be used as the getter function.</p> 363 * </dd> 364 * 365 * <dt>validator <Function | String></dt> 366 * <dd> 367 * <p> 368 * The validator function invoked prior to setting the stored value. Returning 369 * false from the validator function will prevent the value from being stored. 370 * </p> 371 * 372 * <p>validator can also be set to a string, representing the name of the instance method to be used as the validator function.</p> 373 * </dd> 374 * 375 * <dt>lazyAdd <boolean></dt> 376 * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it. 377 * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through 378 * the <a href="#method_addAttrs">addAttrs</a> method.</dd> 379 * 380 * </dl> 381 * 382 * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with 383 * the context ("this") set to the host object.</p> 384 * 385 * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, 386 * and are not intended for public use.</p> 387 * 388 * @method addAttr 389 * 390 * @param {String} name The name of the attribute. 391 * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute. 392 * 393 * <p> 394 * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need 395 * to protect the original values, you will need to merge the object. 396 * </p> 397 * 398 * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set). 399 * 400 * @return {Object} A reference to the host object. 401 * 402 * @chainable 403 */ 404 addAttr : function(name, config, lazy) { 405 406 407 var host = this, // help compression 408 state = host._state, 409 data = state.data, 410 value, 411 added, 412 hasValue; 413 414 config = config || {}; 415 416 if (LAZY_ADD in config) { 417 lazy = config[LAZY_ADD]; 418 } 419 420 added = state.get(name, ADDED); 421 422 if (lazy && !added) { 423 state.data[name] = { 424 lazy : config, 425 added : true 426 }; 427 } else { 428 429 430 if (!added || config.isLazyAdd) { 431 432 hasValue = (VALUE in config); 433 434 435 if (hasValue) { 436 437 // We'll go through set, don't want to set value in config directly 438 439 // PERF TODO: VALIDATE: See if setting this to undefined is sufficient. We use to delete before. 440 // In certain code paths/use cases, undefined may not be the same as not present. 441 // If not, we can set it to some known fixed value (like INVALID_VALUE, say INITIALIZING_VALUE) for performance, 442 // to avoid a delete which seems to help a lot. 443 444 value = config.value; 445 config.value = undefined; 446 } 447 448 config.added = true; 449 config.initializing = true; 450 451 data[name] = config; 452 453 if (hasValue) { 454 // Go through set, so that raw values get normalized/validated 455 host.set(name, value); 456 } 457 458 config.initializing = false;
459 } 460 } 461 462 return host; 463 }, 464 465 /** 466 * Checks if the given attribute has been added to the host 467 * 468 * @method attrAdded 469 * @param {String} name The name of the attribute to check. 470 * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. 471 * This method will return true for lazily added attributes. 472 */ 473 attrAdded: function(name) { 474 return !!(this._state.get(name, ADDED)); 475 }, 476 477 /** 478 * Returns the current value of the attribute. If the attribute 479 * has been configured with a 'getter' function, this method will delegate 480 * to the 'getter' to obtain the value of the attribute. 481 * 482 * @method get 483 * 484 * @param {String} name The name of the attribute. If the value of the attribute is an Object, 485 * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>) 486 * 487 * @return {Any} The value of the attribute 488 */ 489 get : function(name) { 490 return this._getAttr(name); 491 }, 492 493 /** 494 * Checks whether or not the attribute is one which has been 495 * added lazily and still requires initialization. 496 * 497 * @method _isLazyAttr 498 * @private 499 * @param {String} name The name of the attribute 500 * @return {boolean} true if it's a lazily added attribute, false otherwise. 501 */ 502 _isLazyAttr: function(name) { 503 return this._state.get(name, LAZY); 504 }, 505 506 /** 507 * Finishes initializing an attribute which has been lazily added. 508 * 509 * @method _addLazyAttr 510 * @private 511 * @param {Object} name The name of the attribute 512 * @param {Object} [lazyCfg] Optional config hash for the attribute. This is added for performance 513 * along the critical path, where the calling method has already obtained lazy config from state. 514 */ 515 _addLazyAttr: function(name, lazyCfg) { 516 var state = this._state; 517 518 lazyCfg = lazyCfg || state.get(name, LAZY); 519 520 if (lazyCfg) { 521 522 // PERF TODO: For App's id override, otherwise wouldn't be 523 // needed. It expects to find it in the cfg for it's 524 // addAttr override. Would like to remove, once App override is 525 // removed. 526 state.data[name].lazy = undefined; 527 528 lazyCfg.isLazyAdd = true; 529 530 this.addAttr(name, lazyCfg); 531 } 532 }, 533 534 /** 535 * Sets the value of an attribute. 536 * 537 * @method set 538 * @chainable 539 * 540 * @param {String} name The name of the attribute. If the 541 * current value of the attribute is an Object, dot notation can be used 542 * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>). 543 * @param {Any} value The value to set the attribute to. 544 * @param {Object} [opts] Optional data providing the circumstances for the change. 545 * @return {Object} A reference to the host object. 546 */ 547 set : function(name, val, opts) { 548 return this._setAttr(name, val, opts); 549 }, 550 551 /** 552 * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details. 553 * 554 * @method _set 555 * @protected 556 * @chainable 557 * 558 * @param {String} name The name of the attribute. 559 * @param {Any} val The value to set the attribute to. 560 * @param {Object} [opts] Optional data providing the circumstances for the change. 561 * @return {Object} A reference to the host object. 562 */ 563 _set : function(name, val, opts) { 564 return this._setAttr(name, val, opts, true); 565 }, 566 567 /** 568 * Provides the common implementation for the public set and protected _set methods. 569 * 570 * See <a href="#method_set">set</a> for argument details. 571 * 572 * @method _setAttr 573 * @protected 574 * @chainable 575 * 576 * @param {String} name The name of the attribute. 577 * @param {Any} value The value to set the attribute to. 578 * @param {Object} [opts] Optional data providing the circumstances for the change. 579 * @param {boolean} force If true, allows the caller to set values for 580 * readOnly or writeOnce attributes which have already been set. 581 * 582 * @return {Object} A reference to the host object. 583 */ 584 _setAttr : function(name, val, opts, force) { 585 var allowSet = true, 586 state = this._state, 587 stateProxy = this._stateProxy, 588 tCfgs = this._tCfgs, 589 cfg, 590 initialSet, 591 strPath, 592 path, 593 currVal, 594 writeOnce, 595 initializing; 596 597 if (name.indexOf(DOT) !== -1) { 598 strPath = name; 599 600 path = name.split(DOT); 601 name = path.shift(); 602 } 603 604 // On Demand - Should be rare - handles out of order valueFn, setter, getter references 605 if (tCfgs && tCfgs[name]) { 606 this._addOutOfOrder(name, tCfgs[name]); 607 } 608 609 cfg = state.data[name] || {}; 610 611 if (cfg.lazy) { 612 cfg = cfg.lazy; 613 this._addLazyAttr(name, cfg); 614 } 615 616 initialSet = (cfg.value === undefined); 617 618 if (stateProxy && name in stateProxy && !cfg._bypassProxy) { 619 // 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? 620 initialSet = false;
621 } 622 623 writeOnce = cfg.writeOnce; 624 initializing = cfg.initializing; 625 626 if (!initialSet && !force) { 627 628 if (writeOnce) { 629 allowSet = false; 630 } 631 632 if (cfg.readOnly) { 633 allowSet = false; 634 } 635 } 636 637 if (!initializing && !force && writeOnce === INIT_ONLY) { 638 allowSet = false; 639 } 640 641 if (allowSet) { 642 // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value) 643 if (!initialSet) { 644 currVal = this.get(name); 645 } 646 647 if (path) { 648 var copyVal = [currVal].reduce( 649 function(retVal, currVal) { 650 Object.keys(currVal).forEach( 651 function(item) { 652 retVal[item] = currVal[item]; 653 } 654 ); 655 return retVal; 656 }, 657 {} 658 ); 659 660 var pathNode = copyVal; 661 var leafIdx = path.length - 1; 662 663 for (var i = 0; i < leafIdx && pathNode; i++) { 664 pathNode = pathNode[path[i]]; 665 } 666 667 if (pathNode) { 668 delete pathNode[path[leafIdx]]; 669 } 670 671 val = O.setValue(Y.clone(copyVal), path, val); 672 673 if (val === undefined) { 674 allowSet = false; 675 } 676 } 677 678 if (allowSet) { 679 if (!this._fireAttrChange || initializing) { 680 this._setAttrVal(name, strPath, currVal, val, opts, cfg); 681 } else { 682 // HACK - no real reason core needs to know about _fireAttrChange, but 683 // it adds fn hops if we want to break it out. Not sure it's worth it for this critical path 684 this._fireAttrChange(name, strPath, currVal, val, opts, cfg); 685 } 686 } 687 } 688 689 return this; 690 }, 691 692 /** 693 * Utility method used by get/set to add attributes 694 * encountered out of order when calling addAttrs(). 695 * 696 * For example, if: 697 * 698 * this.addAttrs({ 699 * foo: { 700 * setter: function() { 701 * // make sure this bar is available when foo is added 702 * this.get("bar"); 703 * } 704 * }, 705 * bar: { 706 * value: ... 707 * } 708 * }); 709 * 710 * @method _addOutOfOrder 711 * @private 712 * @param name {String} attribute name 713 * @param cfg {Object} attribute configuration 714 */ 715 _addOutOfOrder : function(name, cfg) { 716 717 var attrs = {}; 718 attrs[name] = cfg; 719 720 delete this._tCfgs[name]; 721 722 // TODO: The original code went through addAttrs, so 723 // sticking with it for this pass. Seems like we could 724 // just jump straight to _addAttr() and get some perf 725 // improvement. 726 this._addAttrs(attrs, this._tVals); 727 }, 728 729 /** 730 * Provides the common implementation for the public get method, 731 * allowing Attribute hosts to over-ride either method. 732 * 733 * See <a href="#method_get">get</a> for argument details. 734 * 735 * @method _getAttr 736 * @protected 737 * @chainable 738 * 739 * @param {String} name The name of the attribute. 740 * @return {Any} The value of the attribute. 741 */ 742 _getAttr : function(name) { 743 var fullName = name, 744 tCfgs = this._tCfgs, 745 path, 746 getter, 747 val, 748 attrCfg; 749 750 if (name.indexOf(DOT) !== -1) { 751 path = name.split(DOT); 752 name = path.shift(); 753 } 754 755 // On Demand - Should be rare - handles out of 756 // order valueFn, setter, getter references 757 if (tCfgs && tCfgs[name]) { 758 this._addOutOfOrder(name, tCfgs[name]); 759 } 760 761 attrCfg = this._state.data[name] || {}; 762 763 // Lazy Init 764 if (attrCfg.lazy) { 765 attrCfg = attrCfg.lazy; 766 this._addLazyAttr(name, attrCfg); 767 } 768 769 val = this._getStateVal(name, attrCfg); 770 771 getter = attrCfg.getter; 772 773 if (getter && !getter.call) { 774 getter = this[getter]; 775 } 776 777 val = (getter) ? getter.call(this, val, fullName) : val; 778 val = (path) ? O.getValue(val, path) : val; 779 780 return val; 781 }, 782 783 /** 784 * Gets the stored value for the attribute, from either the 785 * internal state object, or the state proxy if it exits 786 * 787 * @method _getStateVal 788 * @private 789 * @param {String} name The name of the attribute 790 * @param {Object} [cfg] Optional config hash for the attribute. This is added for performance along the critical path, 791 * where the calling method has already obtained the config from state. 792 * 793 * @return {Any} The stored value of the attribute 794 */ 795 _getStateVal : function(name, cfg) { 796 var stateProxy = this._stateProxy; 797 798 if (!cfg) { 799 cfg = this._state.getAll(name) || {}; 800 } 801 802 return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value; 803 }, 804 805 /** 806 * Sets the stored value for the attribute, in either the 807 * internal state object, or the state proxy if it exits 808 * 809 * @method _setStateVal 810 * @private 811 * @param {String} name The name of the attribute 812 * @param {Any} value The value of the attribute 813 */ 814 _setStateVal : function(name, value) { 815 var stateProxy = this._stateProxy; 816 if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) { 817 stateProxy[name] = value; 818 } else { 819 this._state.add(name, VALUE, value); 820 } 821 }, 822 823 /** 824 * Updates the stored value of the attribute in the privately held State object, 825 * if validation and setter passes. 826 * 827 * @method _setAttrVal 828 * @private 829 * @param {String} attrName The attribute name. 830 * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z"). 831 * @param {Any} prevVal The currently stored value of the attribute. 832 * @param {Any} newVal The value which is going to be stored. 833 * @param {Object} [opts] Optional data providing the circumstances for the change. 834 * @param {Object} [attrCfg] Optional config hash for the attribute. This is added for performance along the critical path, 835 * where the calling method has already obtained the config from state. 836 * 837 * @return {Boolean} true if the new attribute value was stored, false if not. 838 */ 839 _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) { 840 841 var host = this, 842 allowSet = true, 843 cfg = attrCfg || this._state.data[attrName] || {}, 844 validator = cfg.validator, 845 setter = cfg.setter, 846 initializing = cfg.initializing, 847 prevRawVal = this._getStateVal(attrName, cfg), 848 name = subAttrName || attrName, 849 retVal, 850 valid; 851 852 if (validator) { 853 if (!validator.call) { 854 // Assume string - trying to keep critical path tight, so avoiding Lang check 855 validator = this[validator]; 856 } 857 if (validator) { 858 valid = validator.call(host, newVal, name, opts); 859 860 if (!valid && initializing) { 861 newVal = cfg.defaultValue; 862 valid = true; // Assume it's valid, for perf. 863 } 864 } 865 } 866 867 if (!validator || valid) { 868 if (setter) { 869 if (!setter.call) { 870 // Assume string - trying to keep critical path tight, so avoiding Lang check 871 setter = this[setter]; 872 } 873 if (setter) { 874 retVal = setter.call(host, newVal, name, opts); 875 876 if (retVal === INVALID_VALUE) { 877 if (initializing) { 878 newVal = cfg.defaultValue; 879 } else { 880 allowSet = false;
881 } 882 } else if (retVal !== undefined){ 883 newVal = retVal; 884 } 885 } 886 } 887 888 if (allowSet) { 889 if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) { 890 allowSet = false; 891 } else { 892 // Store value 893 if (!(INIT_VALUE in cfg)) { 894 cfg.initValue = newVal; 895 } 896 host._setStateVal(attrName, newVal); 897 } 898 } 899 900 } else { 901 allowSet = false; 902 } 903 904 return allowSet; 905 }, 906 907 /** 908 * Sets multiple attribute values. 909 * 910 * @method setAttrs 911 * @param {Object} attrs An object with attributes name/value pairs. 912 * @param {Object} [opts] Optional data providing the circumstances for the change. 913 * @return {Object} A reference to the host object. 914 * @chainable 915 */ 916 setAttrs : function(attrs, opts) { 917 return this._setAttrs(attrs, opts); 918 }, 919 920 /** 921 * Implementation behind the public setAttrs method, to set multiple attribute values. 922 * 923 * @method _setAttrs 924 * @protected 925 * @param {Object} attrs An object with attributes name/value pairs. 926 * @param {Object} [opts] Optional data providing the circumstances for the change 927 * @return {Object} A reference to the host object. 928 * @chainable 929 */ 930 _setAttrs : function(attrs, opts) { 931 var attr; 932 for (attr in attrs) { 933 if ( attrs.hasOwnProperty(attr) ) { 934 this.set(attr, attrs[attr], opts); 935 } 936 } 937 return this; 938 }, 939 940 /** 941 * Gets multiple attribute values. 942 * 943 * @method getAttrs 944 * @param {String[]|Boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are 945 * returned. If set to true, all attributes modified from their initial values are returned. 946 * @return {Object} An object with attribute name/value pairs. 947 */ 948 getAttrs : function(attrs) { 949 return this._getAttrs(attrs); 950 }, 951 952 /** 953 * Implementation behind the public getAttrs method, to get multiple attribute values. 954 * 955 * @method _getAttrs 956 * @protected 957 * @param {String[]|Boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are 958 * returned. If set to true, all attributes modified from their initial values are returned. 959 * @return {Object} An object with attribute name/value pairs. 960 */ 961 _getAttrs : function(attrs) { 962 var obj = {}, 963 attr, i, len, 964 modifiedOnly = (attrs === true); 965 966 // TODO - figure out how to get all "added" 967 if (!attrs || modifiedOnly) { 968 attrs = O.keys(this._state.data); 969 } 970 971 for (i = 0, len = attrs.length; i < len; i++) { 972 attr = attrs[i]; 973 974 if (!modifiedOnly || this._getStateVal(attr) != this._state.get(attr, INIT_VALUE)) { 975 // Go through get, to honor cloning/normalization 976 obj[attr] = this.get(attr); 977 } 978 } 979 980 return obj; 981 }, 982 983 /** 984 * Configures a group of attributes, and sets initial values. 985 * 986 * <p> 987 * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning. 988 * The caller is responsible for merging/cloning the configuration object if required. 989 * </p> 990 * 991 * @method addAttrs 992 * @chainable 993 * 994 * @param {Object} cfgs An object with attribute name/configuration pairs. 995 * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply. 996 * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only. 997 * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
998 * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration. 999 * See <a href="#method_addAttr">addAttr</a>. 1000 * 1001 * @return {Object} A reference to the host object. 1002 */ 1003 addAttrs : function(cfgs, values, lazy) { 1004 if (cfgs) { 1005 this._tCfgs = cfgs; 1006 this._tVals = (values) ? this._normAttrVals(values) : null; 1007 this._addAttrs(cfgs, this._tVals, lazy); 1008 this._tCfgs = this._tVals = null; 1009 } 1010 1011 return this; 1012 }, 1013 1014 /** 1015 * Implementation behind the public addAttrs method. 1016 * 1017 * This method is invoked directly by get if it encounters a scenario 1018 * in which an attribute's valueFn attempts to obtain the 1019 * value an attribute in the same group of attributes, which has not yet 1020 * been added (on demand initialization). 1021 * 1022 * @method _addAttrs 1023 * @private 1024 * @param {Object} cfgs An object with attribute name/configuration pairs. 1025 * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply. 1026 * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only. 1027 * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set. 1028 * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration. 1029 * See <a href="#method_addAttr">addAttr</a>. 1030 */ 1031 _addAttrs : function(cfgs, values, lazy) { 1032 var tCfgs = this._tCfgs, 1033 tVals = this._tVals, 1034 attr, 1035 attrCfg, 1036 value; 1037 1038 for (attr in cfgs) { 1039 if (cfgs.hasOwnProperty(attr)) { 1040 1041 // Not Merging. Caller is responsible for isolating configs 1042 attrCfg = cfgs[attr]; 1043 attrCfg.defaultValue = attrCfg.value; 1044 1045 // Handle simple, complex and user values, accounting for read-only 1046 value = this._getAttrInitVal(attr, attrCfg, tVals); 1047 1048 if (value !== undefined) { 1049 attrCfg.value = value; 1050 } 1051 1052 if (tCfgs[attr]) { 1053 tCfgs[attr] = undefined; 1054 } 1055 1056 this.addAttr(attr, attrCfg, lazy); 1057 } 1058 } 1059 }, 1060 1061 /** 1062 * Utility method to protect an attribute configuration 1063 * hash, by merging the entire object and the individual 1064 * attr config objects. 1065 * 1066 * @method _protectAttrs 1067 * @protected 1068 * @param {Object} attrs A hash of attribute to configuration object pairs. 1069 * @return {Object} A protected version of the attrs argument. 1070 * @deprecated Use `AttributeCore.protectAttrs()` or 1071 * `Attribute.protectAttrs()` which are the same static utility method. 1072 */ 1073 _protectAttrs : AttributeCore.protectAttrs, 1074 1075 /** 1076 * Utility method to normalize attribute values. The base implementation 1077 * simply merges the hash to protect the original. 1078 * 1079 * @method _normAttrVals 1080 * @param {Object} valueHash An object with attribute name/value pairs 1081 * 1082 * @return {Object} An object literal with 2 properties - "simple" and "complex", 1083 * containing simple and complex attribute values respectively keyed 1084 * by the top level attribute name, or null, if valueHash is falsey. 1085 * 1086 * @private 1087 */ 1088 _normAttrVals : function(valueHash) { 1089 var vals, 1090 subvals, 1091 path, 1092 attr, 1093 v, k; 1094 1095 if (!valueHash) { 1096 return null; 1097 } 1098 1099 vals = {}; 1100 1101 for (k in valueHash) { 1102 if (valueHash.hasOwnProperty(k)) { 1103 if (k.indexOf(DOT) !== -1) { 1104 path = k.split(DOT); 1105 attr = path.shift(); 1106 1107 subvals = subvals || {}; 1108 1109 v = subvals[attr] = subvals[attr] || []; 1110 v[v.length] = { 1111 path : path, 1112 value: valueHash[k] 1113 }; 1114 } else { 1115 vals[k] = valueHash[k]; 1116 } 1117 } 1118 } 1119 1120 return { simple:vals, complex:subvals }; 1121 }, 1122 1123 /** 1124 * Returns the initial value of the given attribute from 1125 * either the default configuration provided, or the 1126 * over-ridden value if it exists in the set of initValues 1127 * provided and the attribute is not read-only. 1128 * 1129 * @param {String} attr The name of the attribute 1130 * @param {Object} cfg The attribute configuration object 1131 * @param {Object}
1131 initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals 1132 * 1133 * @return {Any} The initial value of the attribute. 1134 * 1135 * @method _getAttrInitVal 1136 * @private 1137 */ 1138 _getAttrInitVal : function(attr, cfg, initValues) { 1139 var val = cfg.value, 1140 valFn = cfg.valueFn, 1141 tmpVal, 1142 initValSet = false, 1143 readOnly = cfg.readOnly, 1144 simple, 1145 complex, 1146 i, 1147 l, 1148 path, 1149 subval, 1150 subvals; 1151 1152 if (!readOnly && initValues) { 1153 // Simple Attributes 1154 simple = initValues.simple; 1155 if (simple && simple.hasOwnProperty(attr)) { 1156 val = simple[attr]; 1157 initValSet = true; 1158 } 1159 } 1160 1161 if (valFn && !initValSet) { 1162 if (!valFn.call) { 1163 valFn = this[valFn]; 1164 } 1165 if (valFn) { 1166 tmpVal = valFn.call(this, attr); 1167 val = tmpVal; 1168 } 1169 } 1170 1171 if (!readOnly && initValues) { 1172 1173 // Complex Attributes (complex values applied, after simple, in case both are set) 1174 complex = initValues.complex; 1175 1176 if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) { 1177 subvals = complex[attr]; 1178 for (i = 0, l = subvals.length; i < l; ++i) { 1179 path = subvals[i].path; 1180 subval = subvals[i].value; 1181 O.setValue(val, path, subval); 1182 } 1183 } 1184 } 1185 1186 return val; 1187 }, 1188 1189 /** 1190 * Utility method to set up initial attributes defined during construction, 1191 * either through the constructor.ATTRS property, or explicitly passed in. 1192 * 1193 * @method _initAttrs 1194 * @protected 1195 * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>). 1196 * These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor. 1197 * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>). 1198 * These are not merged/cloned. The caller is responsible for isolating user provided values if required. 1199 * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>). 1200 */ 1201 _initAttrs : function(attrs, values, lazy) { 1202 // ATTRS support for Node, which is not Base based 1203 attrs = attrs || this.constructor.ATTRS; 1204 1205 var Base = Y.Base, 1206 BaseCore = Y.BaseCore, 1207 baseInst = (Base && Y.instanceOf(this, Base)), 1208 baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore)); 1209 1210 if (attrs && !baseInst && !baseCoreInst) { 1211 this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy); 1212 } 1213 } 1214 }; 1215 1216 Y.AttributeCore = AttributeCore; 1217 1218 1219}, 'patched-v3.18.7', {"requires": ["oop"]});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.