[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('attribute-core', function (Y, NAME) { 9 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 }; 28 29 Y.State.prototype = { 30 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]; 41 42 if (!item) { 43 item = this.data[name] = {}; 44 } 45 46 item[key] = val; 47 }, 48 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; 59 60 if (!item) { 61 item = this.data[name] = {}; 62 } 63 64 for (key in obj) { 65 if (obj.hasOwnProperty(key)) { 66 item[key] = obj[key]; 67 } 68 } 69 }, 70 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]; 80 81 if (item) { 82 delete item[key]; 83 } 84 }, 85 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; 95 96 if (!obj) { 97 data = this.data; 98 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 }, 108 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]; 119 120 if (item) { 121 return item[key]; 122 } 123 }, 124 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; 141 142 if (reference) { 143 obj = item; 144 } else if (item) { 145 obj = {}; 146 147 for (key in item) { 148 if (item.hasOwnProperty(key)) { 149 obj[key] = item[key]; 150 } 151 } 152 } 153 154 return obj; 155 } 156 }; 157 /*For log lines*/ 158 /*jshint maxlen:200*/ 159 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 */ 169 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, 180 181 DOT = ".", 182 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", 193 194 // Used for internal state management 195 ADDED = "added", 196 BYPASS_PROXY = "_bypassProxy", 197 INIT_VALUE = "initValue", 198 LAZY = "lazy", 199 200 INVALID_VALUE; 201 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; 239 240 this._initAttrHost(attrs, values, lazy); 241 } 242 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; 257 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]; 271 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 } 290 291 return attrs; 292 }; 293 294 AttributeCore.prototype = { 295 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 }, 312 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 <Any></dt> 323 * <dd>The initial value to set on the attribute</dd> 324 * 325 * <dt>valueFn <Function | String></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 <boolean></dt> 337 * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true 338 * cannot be modified by invoking the set method.</dd> 339 * 340 * <dt>writeOnce <boolean> or <string></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 <Function | String></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 <Function | String></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 <Function | String></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 <boolean></dt> 383 * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it. 384 * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through 385 * the <a href="#method_addAttrs">addAttrs</a> method.</dd> 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) { 412 413 Y.log('Adding attribute: ' + name, 'info', 'attribute'); 414 415 var host = this, // help compression 416 state = host._state, 417 data = state.data, 418 value, 419 added, 420 hasValue; 421 422 config = config || {}; 423 424 if (LAZY_ADD in config) { 425 lazy = config[LAZY_ADD]; 426 } 427 428 added = state.get(name, ADDED); 429 430 if (lazy && !added) { 431 state.data[name] = { 432 lazy : config, 433 added : true 434 }; 435 } else { 436 437 if (added && !config.isLazyAdd) { Y.log('Attribute: ' + name + ' already exists. Cannot add it again without removing it first', 'warn', 'attribute'); } 438 439 if (!added || config.isLazyAdd) { 440 441 hasValue = (VALUE in config); 442 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');} 444 445 if (hasValue) { 446 447 // We'll go through set, don't want to set value in config directly 448 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. 453 454 value = config.value; 455 config.value = undefined; 456 } 457 458 config.added = true; 459 config.initializing = true; 460 461 data[name] = config; 462 463 if (hasValue) { 464 // Go through set, so that raw values get normalized/validated 465 host.set(name, value); 466 } 467 468 config.initializing = false; 469 } 470 } 471 472 return host; 473 }, 474 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 }, 486 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 }, 502 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 }, 515 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; 527 528 lazyCfg = lazyCfg || state.get(name, LAZY); 529 530 if (lazyCfg) { 531 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; 537 538 lazyCfg.isLazyAdd = true; 539 540 this.addAttr(name, lazyCfg); 541 } 542 }, 543 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 }, 560 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 }, 576 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; 606 607 if (name.indexOf(DOT) !== -1) { 608 strPath = name; 609 610 path = name.split(DOT); 611 name = path.shift(); 612 } 613 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 } 618 619 cfg = state.data[name] || {}; 620 621 if (cfg.lazy) { 622 cfg = cfg.lazy; 623 this._addLazyAttr(name, cfg); 624 } 625 626 initialSet = (cfg.value === undefined); 627 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 } 632 633 writeOnce = cfg.writeOnce; 634 initializing = cfg.initializing; 635 636 if (!initialSet && !force) { 637 638 if (writeOnce) { 639 Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute'); 640 allowSet = false; 641 } 642 643 if (cfg.readOnly) { 644 Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute'); 645 allowSet = false; 646 } 647 } 648 649 if (!initializing && !force && writeOnce === INIT_ONLY) { 650 Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce: "initOnly"', 'warn', 'attribute'); 651 allowSet = false; 652 } 653 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 } 659 660 if (path) { 661 val = O.setValue(Y.clone(currVal), path, val); 662 663 if (val === undefined) { 664 Y.log('Set attribute path:' + strPath + ', aborted; Path is invalid', 'warn', 'attribute'); 665 allowSet = false; 666 } 667 } 668 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 } 679 680 return this; 681 }, 682 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) { 707 708 var attrs = {}; 709 attrs[name] = cfg; 710 711 delete this._tCfgs[name]; 712 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 }, 719 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; 740 741 if (name.indexOf(DOT) !== -1) { 742 path = name.split(DOT); 743 name = path.shift(); 744 } 745 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 } 751 752 attrCfg = this._state.data[name] || {}; 753 754 // Lazy Init 755 if (attrCfg.lazy) { 756 attrCfg = attrCfg.lazy; 757 this._addLazyAttr(name, attrCfg); 758 } 759 760 val = this._getStateVal(name, attrCfg); 761 762 getter = attrCfg.getter; 763 764 if (getter && !getter.call) { 765 getter = this[getter]; 766 } 767 768 val = (getter) ? getter.call(this, val, fullName) : val; 769 val = (path) ? O.getValue(val, path) : val; 770 771 return val; 772 }, 773 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; 788 789 if (!cfg) { 790 cfg = this._state.getAll(name) || {}; 791 } 792 793 return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value; 794 }, 795 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 }, 813 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 {Boolean} true if the new attribute value was stored, false if not. 829 */ 830 _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) { 831 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; 842 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); 850 851 if (!valid && initializing) { 852 newVal = cfg.defaultValue; 853 valid = true; // Assume it's valid, for perf. 854 } 855 } 856 } 857 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); 866 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 } 881 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 } 894 895 } else { 896 Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute'); 897 allowSet = false; 898 } 899 900 return allowSet; 901 }, 902 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 }, 915 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 }, 935 936 /** 937 * Gets multiple attribute values. 938 * 939 * @method getAttrs 940 * @param {String[]|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 }, 947 948 /** 949 * Implementation behind the public getAttrs method, to get multiple attribute values. 950 * 951 * @method _getAttrs 952 * @protected 953 * @param {String[]|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); 961 962 // TODO - figure out how to get all "added" 963 if (!attrs || modifiedOnly) { 964 attrs = O.keys(this._state.data); 965 } 966 967 for (i = 0, len = attrs.length; i < len; i++) { 968 attr = attrs[i]; 969 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 } 975 976 return obj; 977 }, 978 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 } 1006 1007 return this; 1008 }, 1009 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; 1033 1034 for (attr in cfgs) { 1035 if (cfgs.hasOwnProperty(attr)) { 1036 1037 // Not Merging. Caller is responsible for isolating configs 1038 attrCfg = cfgs[attr]; 1039 attrCfg.defaultValue = attrCfg.value; 1040 1041 // Handle simple, complex and user values, accounting for read-only 1042 value = this._getAttrInitVal(attr, attrCfg, tVals); 1043 1044 if (value !== undefined) { 1045 attrCfg.value = value; 1046 } 1047 1048 if (tCfgs[attr]) { 1049 tCfgs[attr] = undefined; 1050 } 1051 1052 this.addAttr(attr, attrCfg, lazy); 1053 } 1054 } 1055 }, 1056 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, 1070 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; 1090 1091 if (!valueHash) { 1092 return null; 1093 } 1094 1095 vals = {}; 1096 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(); 1102 1103 subvals = subvals || {}; 1104 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 } 1115 1116 return { simple:vals, complex:subvals }; 1117 }, 1118 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; 1147 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 } 1156 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 } 1166 1167 if (!readOnly && initValues) { 1168 1169 // Complex Attributes (complex values applied, after simple, in case both are set) 1170 complex = initValues.complex; 1171 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 } 1181 1182 return val; 1183 }, 1184 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; 1200 1201 var Base = Y.Base, 1202 BaseCore = Y.BaseCore, 1203 baseInst = (Base && Y.instanceOf(this, Base)), 1204 baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore)); 1205 1206 if (attrs && !baseInst && !baseCoreInst) { 1207 this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy); 1208 } 1209 } 1210 }; 1211 1212 Y.AttributeCore = AttributeCore; 1213 1214 1215 }, '3.17.2', {"requires": ["oop"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |