[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/attribute-core/ -> attribute-core.js (source)

   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 &#60;Any&#62;</dt>
 323           *    <dd>The initial value to set on the attribute</dd>
 324           *
 325           *    <dt>valueFn &#60;Function | String&#62;</dt>
 326           *    <dd>
 327           *    <p>A function, which will return the initial value to set on the attribute. This is useful
 328           *    for cases where the attribute configuration is defined statically, but needs to
 329           *    reference the host instance ("this") to obtain an initial value. If both the value and valueFn properties are defined,
 330           *    the value returned by the valueFn has precedence over the value property, unless it returns undefined, in which
 331           *    case the value property is used.</p>
 332           *
 333           *    <p>valueFn can also be set to a string, representing the name of the instance method to be used to retrieve the value.</p>
 334           *    </dd>
 335           *
 336           *    <dt>readOnly &#60;boolean&#62;</dt>
 337           *    <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
 338           *        cannot be modified by invoking the set method.</dd>
 339           *
 340           *    <dt>writeOnce &#60;boolean&#62; or &#60;string&#62;</dt>
 341           *    <dd>
 342           *        Whether or not the attribute is "write once". Attributes having writeOnce set to true,
 343           *        can only have their values set once, be it through the default configuration,
 344           *        constructor configuration arguments, or by invoking set.
 345           *        <p>The writeOnce attribute can also be set to the string "initOnly",
 346           *         in which case the attribute can only be set during initialization
 347           *        (when used with Base, this means it can only be set during construction)</p>
 348           *    </dd>
 349           *
 350           *    <dt>setter &#60;Function | String&#62;</dt>
 351           *    <dd>
 352           *    <p>The setter function used to massage or normalize the value passed to the set method for the attribute.
 353           *    The value returned by the setter will be the final stored value. Returning
 354           *    <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
 355           *    the value from being stored.
 356           *    </p>
 357           *
 358           *    <p>setter can also be set to a string, representing the name of the instance method to be used as the setter function.</p>
 359           *    </dd>
 360           *
 361           *    <dt>getter &#60;Function | String&#62;</dt>
 362           *    <dd>
 363           *    <p>
 364           *    The getter function used to massage or normalize the value returned by the get method for the attribute.
 365           *    The value returned by the getter function is the value which will be returned to the user when they
 366           *    invoke get.
 367           *    </p>
 368           *
 369           *    <p>getter can also be set to a string, representing the name of the instance method to be used as the getter function.</p>
 370           *    </dd>
 371           *
 372           *    <dt>validator &#60;Function | String&#62;</dt>
 373           *    <dd>
 374           *    <p>
 375           *    The validator function invoked prior to setting the stored value. Returning
 376           *    false from the validator function will prevent the value from being stored.
 377           *    </p>
 378           *
 379           *    <p>validator can also be set to a string, representing the name of the instance method to be used as the validator function.</p>
 380           *    </dd>
 381           *
 382           *    <dt>lazyAdd &#60;boolean&#62;</dt>
 383           *    <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
 384           *    This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
 385           *    the <a href="#method_addAttrs">addAttrs</a> method.</dd>
 386           *
 387           * </dl>
 388           *
 389           * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
 390           * the context ("this") set to the host object.</p>
 391           *
 392           * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute,
 393           * and are not intended for public use.</p>
 394           *
 395           * @method addAttr
 396           *
 397           * @param {String} name The name of the attribute.
 398           * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
 399           *
 400           * <p>
 401           * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
 402           * to protect the original values, you will need to merge the object.
 403           * </p>
 404           *
 405           * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
 406           *
 407           * @return {Object} A reference to the host object.
 408           *
 409           * @chainable
 410           */
 411          addAttr : function(name, config, lazy) {
 412  
 413  
 414              var host = this, // help compression
 415                  state = host._state,
 416                  data = state.data,
 417                  value,
 418                  added,
 419                  hasValue;
 420  
 421              config = config || {};
 422  
 423              if (LAZY_ADD in config) {
 424                  lazy = config[LAZY_ADD];
 425              }
 426  
 427              added = state.get(name, ADDED);
 428  
 429              if (lazy && !added) {
 430                  state.data[name] = {
 431                      lazy : config,
 432                      added : true
 433                  };
 434              } else {
 435  
 436  
 437                  if (!added || config.isLazyAdd) {
 438  
 439                      hasValue = (VALUE in config);
 440  
 441  
 442                      if (hasValue) {
 443  
 444                          // We'll go through set, don't want to set value in config directly
 445  
 446                          // PERF TODO: VALIDATE: See if setting this to undefined is sufficient. We use to delete before.
 447                          // In certain code paths/use cases, undefined may not be the same as not present.
 448                          // If not, we can set it to some known fixed value (like INVALID_VALUE, say INITIALIZING_VALUE) for performance,
 449                          // to avoid a delete which seems to help a lot.
 450  
 451                          value = config.value;
 452                          config.value = undefined;
 453                      }
 454  
 455                      config.added = true;
 456                      config.initializing = true;
 457  
 458                      data[name] = config;
 459  
 460                      if (hasValue) {
 461                          // Go through set, so that raw values get normalized/validated
 462                          host.set(name, value);
 463                      }
 464  
 465                      config.initializing = false;
 466                  }
 467              }
 468  
 469              return host;
 470          },
 471  
 472          /**
 473           * Checks if the given attribute has been added to the host
 474           *
 475           * @method attrAdded
 476           * @param {String} name The name of the attribute to check.
 477           * @return {boolean} true if an attribute with the given name has been added, false if it hasn't.
 478           *         This method will return true for lazily added attributes.
 479           */
 480          attrAdded: function(name) {
 481              return !!(this._state.get(name, ADDED));
 482          },
 483  
 484          /**
 485           * Returns the current value of the attribute. If the attribute
 486           * has been configured with a 'getter' function, this method will delegate
 487           * to the 'getter' to obtain the value of the attribute.
 488           *
 489           * @method get
 490           *
 491           * @param {String} name The name of the attribute. If the value of the attribute is an Object,
 492           * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
 493           *
 494           * @return {Any} The value of the attribute
 495           */
 496          get : function(name) {
 497              return this._getAttr(name);
 498          },
 499  
 500          /**
 501           * Checks whether or not the attribute is one which has been
 502           * added lazily and still requires initialization.
 503           *
 504           * @method _isLazyAttr
 505           * @private
 506           * @param {String} name The name of the attribute
 507           * @return {boolean} true if it's a lazily added attribute, false otherwise.
 508           */
 509          _isLazyAttr: function(name) {
 510              return this._state.get(name, LAZY);
 511          },
 512  
 513          /**
 514           * Finishes initializing an attribute which has been lazily added.
 515           *
 516           * @method _addLazyAttr
 517           * @private
 518           * @param {Object} name The name of the attribute
 519           * @param {Object} [lazyCfg] Optional config hash for the attribute. This is added for performance
 520           * along the critical path, where the calling method has already obtained lazy config from state.
 521           */
 522          _addLazyAttr: function(name, lazyCfg) {
 523              var state = this._state;
 524  
 525              lazyCfg = lazyCfg || state.get(name, LAZY);
 526  
 527              if (lazyCfg) {
 528  
 529                  // PERF TODO: For App's id override, otherwise wouldn't be
 530                  // needed. It expects to find it in the cfg for it's
 531                  // addAttr override. Would like to remove, once App override is
 532                  // removed.
 533                  state.data[name].lazy = undefined;
 534  
 535                  lazyCfg.isLazyAdd = true;
 536  
 537                  this.addAttr(name, lazyCfg);
 538              }
 539          },
 540  
 541          /**
 542           * Sets the value of an attribute.
 543           *
 544           * @method set
 545           * @chainable
 546           *
 547           * @param {String} name The name of the attribute. If the
 548           * current value of the attribute is an Object, dot notation can be used
 549           * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
 550           * @param {Any} value The value to set the attribute to.
 551           * @param {Object} [opts] Optional data providing the circumstances for the change.
 552           * @return {Object} A reference to the host object.
 553           */
 554          set : function(name, val, opts) {
 555              return this._setAttr(name, val, opts);
 556          },
 557  
 558          /**
 559           * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
 560           *
 561           * @method _set
 562           * @protected
 563           * @chainable
 564           *
 565           * @param {String} name The name of the attribute.
 566           * @param {Any} val The value to set the attribute to.
 567           * @param {Object} [opts] Optional data providing the circumstances for the change.
 568           * @return {Object} A reference to the host object.
 569           */
 570          _set : function(name, val, opts) {
 571              return this._setAttr(name, val, opts, true);
 572          },
 573  
 574          /**
 575           * Provides the common implementation for the public set and protected _set methods.
 576           *
 577           * See <a href="#method_set">set</a> for argument details.
 578           *
 579           * @method _setAttr
 580           * @protected
 581           * @chainable
 582           *
 583           * @param {String} name The name of the attribute.
 584           * @param {Any} value The value to set the attribute to.
 585           * @param {Object} [opts] Optional data providing the circumstances for the change.
 586           * @param {boolean} force If true, allows the caller to set values for
 587           * readOnly or writeOnce attributes which have already been set.
 588           *
 589           * @return {Object} A reference to the host object.
 590           */
 591          _setAttr : function(name, val, opts, force)  {
 592              var allowSet = true,
 593                  state = this._state,
 594                  stateProxy = this._stateProxy,
 595                  tCfgs = this._tCfgs,
 596                  cfg,
 597                  initialSet,
 598                  strPath,
 599                  path,
 600                  currVal,
 601                  writeOnce,
 602                  initializing;
 603  
 604              if (name.indexOf(DOT) !== -1) {
 605                  strPath = name;
 606  
 607                  path = name.split(DOT);
 608                  name = path.shift();
 609              }
 610  
 611              // On Demand - Should be rare - handles out of order valueFn, setter, getter references
 612              if (tCfgs && tCfgs[name]) {
 613                  this._addOutOfOrder(name, tCfgs[name]);
 614              }
 615  
 616              cfg = state.data[name] || {};
 617  
 618              if (cfg.lazy) {
 619                  cfg = cfg.lazy;
 620                  this._addLazyAttr(name, cfg);
 621              }
 622  
 623              initialSet = (cfg.value === undefined);
 624  
 625              if (stateProxy && name in stateProxy && !cfg._bypassProxy) {
 626                  // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set?
 627                  initialSet = false;
 628              }
 629  
 630              writeOnce = cfg.writeOnce;
 631              initializing = cfg.initializing;
 632  
 633              if (!initialSet && !force) {
 634  
 635                  if (writeOnce) {
 636                      allowSet = false;
 637                  }
 638  
 639                  if (cfg.readOnly) {
 640                      allowSet = false;
 641                  }
 642              }
 643  
 644              if (!initializing && !force && writeOnce === INIT_ONLY) {
 645                  allowSet = false;
 646              }
 647  
 648              if (allowSet) {
 649                  // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
 650                  if (!initialSet) {
 651                      currVal =  this.get(name);
 652                  }
 653  
 654                  if (path) {
 655                     val = O.setValue(Y.clone(currVal), path, val);
 656  
 657                     if (val === undefined) {
 658                         allowSet = false;
 659                     }
 660                  }
 661  
 662                  if (allowSet) {
 663                      if (!this._fireAttrChange || initializing) {
 664                          this._setAttrVal(name, strPath, currVal, val, opts, cfg);
 665                      } else {
 666                          // HACK - no real reason core needs to know about _fireAttrChange, but
 667                          // it adds fn hops if we want to break it out. Not sure it's worth it for this critical path
 668                          this._fireAttrChange(name, strPath, currVal, val, opts, cfg);
 669                      }
 670                  }
 671              }
 672  
 673              return this;
 674          },
 675  
 676          /**
 677           * Utility method used by get/set to add attributes
 678           * encountered out of order when calling addAttrs().
 679           *
 680           * For example, if:
 681           *
 682           *     this.addAttrs({
 683           *          foo: {
 684           *              setter: function() {
 685           *                 // make sure this bar is available when foo is added
 686           *                 this.get("bar");
 687           *              }
 688           *          },
 689           *          bar: {
 690           *              value: ...
 691           *          }
 692           *     });
 693           *
 694           * @method _addOutOfOrder
 695           * @private
 696           * @param name {String} attribute name
 697           * @param cfg {Object} attribute configuration
 698           */
 699          _addOutOfOrder : function(name, cfg) {
 700  
 701              var attrs = {};
 702              attrs[name] = cfg;
 703  
 704              delete this._tCfgs[name];
 705  
 706              // TODO: The original code went through addAttrs, so
 707              // sticking with it for this pass. Seems like we could
 708              // just jump straight to _addAttr() and get some perf
 709              // improvement.
 710              this._addAttrs(attrs, this._tVals);
 711          },
 712  
 713          /**
 714           * Provides the common implementation for the public get method,
 715           * allowing Attribute hosts to over-ride either method.
 716           *
 717           * See <a href="#method_get">get</a> for argument details.
 718           *
 719           * @method _getAttr
 720           * @protected
 721           * @chainable
 722           *
 723           * @param {String} name The name of the attribute.
 724           * @return {Any} The value of the attribute.
 725           */
 726          _getAttr : function(name) {
 727              var fullName = name,
 728                  tCfgs = this._tCfgs,
 729                  path,
 730                  getter,
 731                  val,
 732                  attrCfg;
 733  
 734              if (name.indexOf(DOT) !== -1) {
 735                  path = name.split(DOT);
 736                  name = path.shift();
 737              }
 738  
 739              // On Demand - Should be rare - handles out of
 740              // order valueFn, setter, getter references
 741              if (tCfgs && tCfgs[name]) {
 742                  this._addOutOfOrder(name, tCfgs[name]);
 743              }
 744  
 745              attrCfg = this._state.data[name] || {};
 746  
 747              // Lazy Init
 748              if (attrCfg.lazy) {
 749                  attrCfg = attrCfg.lazy;
 750                  this._addLazyAttr(name, attrCfg);
 751              }
 752  
 753              val = this._getStateVal(name, attrCfg);
 754  
 755              getter = attrCfg.getter;
 756  
 757              if (getter && !getter.call) {
 758                  getter = this[getter];
 759              }
 760  
 761              val = (getter) ? getter.call(this, val, fullName) : val;
 762              val = (path) ? O.getValue(val, path) : val;
 763  
 764              return val;
 765          },
 766  
 767          /**
 768           * Gets the stored value for the attribute, from either the
 769           * internal state object, or the state proxy if it exits
 770           *
 771           * @method _getStateVal
 772           * @private
 773           * @param {String} name The name of the attribute
 774           * @param {Object} [cfg] Optional config hash for the attribute. This is added for performance along the critical path,
 775           * where the calling method has already obtained the config from state.
 776           *
 777           * @return {Any} The stored value of the attribute
 778           */
 779          _getStateVal : function(name, cfg) {
 780              var stateProxy = this._stateProxy;
 781  
 782              if (!cfg) {
 783                  cfg = this._state.getAll(name) || {};
 784              }
 785  
 786              return (stateProxy && (name in stateProxy) && !(cfg._bypassProxy)) ? stateProxy[name] : cfg.value;
 787          },
 788  
 789          /**
 790           * Sets the stored value for the attribute, in either the
 791           * internal state object, or the state proxy if it exits
 792           *
 793           * @method _setStateVal
 794           * @private
 795           * @param {String} name The name of the attribute
 796           * @param {Any} value The value of the attribute
 797           */
 798          _setStateVal : function(name, value) {
 799              var stateProxy = this._stateProxy;
 800              if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
 801                  stateProxy[name] = value;
 802              } else {
 803                  this._state.add(name, VALUE, value);
 804              }
 805          },
 806  
 807          /**
 808           * Updates the stored value of the attribute in the privately held State object,
 809           * if validation and setter passes.
 810           *
 811           * @method _setAttrVal
 812           * @private
 813           * @param {String} attrName The attribute name.
 814           * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
 815           * @param {Any} prevVal The currently stored value of the attribute.
 816           * @param {Any} newVal The value which is going to be stored.
 817           * @param {Object} [opts] Optional data providing the circumstances for the change.
 818           * @param {Object} [attrCfg] Optional config hash for the attribute. This is added for performance along the critical path,
 819           * where the calling method has already obtained the config from state.
 820           *
 821           * @return {Boolean} true if the new attribute value was stored, false if not.
 822           */
 823          _setAttrVal : function(attrName, subAttrName, prevVal, newVal, opts, attrCfg) {
 824  
 825              var host = this,
 826                  allowSet = true,
 827                  cfg = attrCfg || this._state.data[attrName] || {},
 828                  validator = cfg.validator,
 829                  setter = cfg.setter,
 830                  initializing = cfg.initializing,
 831                  prevRawVal = this._getStateVal(attrName, cfg),
 832                  name = subAttrName || attrName,
 833                  retVal,
 834                  valid;
 835  
 836              if (validator) {
 837                  if (!validator.call) {
 838                      // Assume string - trying to keep critical path tight, so avoiding Lang check
 839                      validator = this[validator];
 840                  }
 841                  if (validator) {
 842                      valid = validator.call(host, newVal, name, opts);
 843  
 844                      if (!valid && initializing) {
 845                          newVal = cfg.defaultValue;
 846                          valid = true; // Assume it's valid, for perf.
 847                      }
 848                  }
 849              }
 850  
 851              if (!validator || valid) {
 852                  if (setter) {
 853                      if (!setter.call) {
 854                          // Assume string - trying to keep critical path tight, so avoiding Lang check
 855                          setter = this[setter];
 856                      }
 857                      if (setter) {
 858                          retVal = setter.call(host, newVal, name, opts);
 859  
 860                          if (retVal === INVALID_VALUE) {
 861                              if (initializing) {
 862                                  newVal = cfg.defaultValue;
 863                              } else {
 864                                  allowSet = false;
 865                              }
 866                          } else if (retVal !== undefined){
 867                              newVal = retVal;
 868                          }
 869                      }
 870                  }
 871  
 872                  if (allowSet) {
 873                      if(!subAttrName && (newVal === prevRawVal) && !Lang.isObject(newVal)) {
 874                          allowSet = false;
 875                      } else {
 876                          // Store value
 877                          if (!(INIT_VALUE in cfg)) {
 878                              cfg.initValue = newVal;
 879                          }
 880                          host._setStateVal(attrName, newVal);
 881                      }
 882                  }
 883  
 884              } else {
 885                  allowSet = false;
 886              }
 887  
 888              return allowSet;
 889          },
 890  
 891          /**
 892           * Sets multiple attribute values.
 893           *
 894           * @method setAttrs
 895           * @param {Object} attrs  An object with attributes name/value pairs.
 896           * @param {Object} [opts] Optional data providing the circumstances for the change.
 897           * @return {Object} A reference to the host object.
 898           * @chainable
 899           */
 900          setAttrs : function(attrs, opts) {
 901              return this._setAttrs(attrs, opts);
 902          },
 903  
 904          /**
 905           * Implementation behind the public setAttrs method, to set multiple attribute values.
 906           *
 907           * @method _setAttrs
 908           * @protected
 909           * @param {Object} attrs  An object with attributes name/value pairs.
 910           * @param {Object} [opts] Optional data providing the circumstances for the change
 911           * @return {Object} A reference to the host object.
 912           * @chainable
 913           */
 914          _setAttrs : function(attrs, opts) {
 915              var attr;
 916              for (attr in attrs) {
 917                  if ( attrs.hasOwnProperty(attr) ) {
 918                      this.set(attr, attrs[attr], opts);
 919                  }
 920              }
 921              return this;
 922          },
 923  
 924          /**
 925           * Gets multiple attribute values.
 926           *
 927           * @method getAttrs
 928           * @param {String[]|Boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
 929           * returned. If set to true, all attributes modified from their initial values are returned.
 930           * @return {Object} An object with attribute name/value pairs.
 931           */
 932          getAttrs : function(attrs) {
 933              return this._getAttrs(attrs);
 934          },
 935  
 936          /**
 937           * Implementation behind the public getAttrs method, to get multiple attribute values.
 938           *
 939           * @method _getAttrs
 940           * @protected
 941           * @param {String[]|Boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
 942           * returned. If set to true, all attributes modified from their initial values are returned.
 943           * @return {Object} An object with attribute name/value pairs.
 944           */
 945          _getAttrs : function(attrs) {
 946              var obj = {},
 947                  attr, i, len,
 948                  modifiedOnly = (attrs === true);
 949  
 950              // TODO - figure out how to get all "added"
 951              if (!attrs || modifiedOnly) {
 952                  attrs = O.keys(this._state.data);
 953              }
 954  
 955              for (i = 0, len = attrs.length; i < len; i++) {
 956                  attr = attrs[i];
 957  
 958                  if (!modifiedOnly || this._getStateVal(attr) != this._state.get(attr, INIT_VALUE)) {
 959                      // Go through get, to honor cloning/normalization
 960                      obj[attr] = this.get(attr);
 961                  }
 962              }
 963  
 964              return obj;
 965          },
 966  
 967          /**
 968           * Configures a group of attributes, and sets initial values.
 969           *
 970           * <p>
 971           * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
 972           * The caller is responsible for merging/cloning the configuration object if required.
 973           * </p>
 974           *
 975           * @method addAttrs
 976           * @chainable
 977           *
 978           * @param {Object} cfgs An object with attribute name/configuration pairs.
 979           * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
 980           * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
 981           * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
 982           * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
 983           * See <a href="#method_addAttr">addAttr</a>.
 984           *
 985           * @return {Object} A reference to the host object.
 986           */
 987          addAttrs : function(cfgs, values, lazy) {
 988              if (cfgs) {
 989                  this._tCfgs = cfgs;
 990                  this._tVals = (values) ? this._normAttrVals(values) : null;
 991                  this._addAttrs(cfgs, this._tVals, lazy);
 992                  this._tCfgs = this._tVals = null;
 993              }
 994  
 995              return this;
 996          },
 997  
 998          /**
 999           * Implementation behind the public addAttrs method.
1000           *
1001           * This method is invoked directly by get if it encounters a scenario
1002           * in which an attribute's valueFn attempts to obtain the
1003           * value an attribute in the same group of attributes, which has not yet
1004           * been added (on demand initialization).
1005           *
1006           * @method _addAttrs
1007           * @private
1008           * @param {Object} cfgs An object with attribute name/configuration pairs.
1009           * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
1010           * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
1011           * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
1012           * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
1013           * See <a href="#method_addAttr">addAttr</a>.
1014           */
1015          _addAttrs : function(cfgs, values, lazy) {
1016              var tCfgs = this._tCfgs,
1017                  tVals = this._tVals,
1018                  attr,
1019                  attrCfg,
1020                  value;
1021  
1022              for (attr in cfgs) {
1023                  if (cfgs.hasOwnProperty(attr)) {
1024  
1025                      // Not Merging. Caller is responsible for isolating configs
1026                      attrCfg = cfgs[attr];
1027                      attrCfg.defaultValue = attrCfg.value;
1028  
1029                      // Handle simple, complex and user values, accounting for read-only
1030                      value = this._getAttrInitVal(attr, attrCfg, tVals);
1031  
1032                      if (value !== undefined) {
1033                          attrCfg.value = value;
1034                      }
1035  
1036                      if (tCfgs[attr]) {
1037                          tCfgs[attr] = undefined;
1038                      }
1039  
1040                      this.addAttr(attr, attrCfg, lazy);
1041                  }
1042              }
1043          },
1044  
1045          /**
1046           * Utility method to protect an attribute configuration
1047           * hash, by merging the entire object and the individual
1048           * attr config objects.
1049           *
1050           * @method _protectAttrs
1051           * @protected
1052           * @param {Object} attrs A hash of attribute to configuration object pairs.
1053           * @return {Object} A protected version of the attrs argument.
1054           * @deprecated Use `AttributeCore.protectAttrs()` or
1055           *   `Attribute.protectAttrs()` which are the same static utility method.
1056           */
1057          _protectAttrs : AttributeCore.protectAttrs,
1058  
1059          /**
1060           * Utility method to normalize attribute values. The base implementation
1061           * simply merges the hash to protect the original.
1062           *
1063           * @method _normAttrVals
1064           * @param {Object} valueHash An object with attribute name/value pairs
1065           *
1066           * @return {Object} An object literal with 2 properties - "simple" and "complex",
1067           * containing simple and complex attribute values respectively keyed
1068           * by the top level attribute name, or null, if valueHash is falsey.
1069           *
1070           * @private
1071           */
1072          _normAttrVals : function(valueHash) {
1073              var vals,
1074                  subvals,
1075                  path,
1076                  attr,
1077                  v, k;
1078  
1079              if (!valueHash) {
1080                  return null;
1081              }
1082  
1083              vals = {};
1084  
1085              for (k in valueHash) {
1086                  if (valueHash.hasOwnProperty(k)) {
1087                      if (k.indexOf(DOT) !== -1) {
1088                          path = k.split(DOT);
1089                          attr = path.shift();
1090  
1091                          subvals = subvals || {};
1092  
1093                          v = subvals[attr] = subvals[attr] || [];
1094                          v[v.length] = {
1095                              path : path,
1096                              value: valueHash[k]
1097                          };
1098                      } else {
1099                          vals[k] = valueHash[k];
1100                      }
1101                  }
1102              }
1103  
1104              return { simple:vals, complex:subvals };
1105          },
1106  
1107          /**
1108           * Returns the initial value of the given attribute from
1109           * either the default configuration provided, or the
1110           * over-ridden value if it exists in the set of initValues
1111           * provided and the attribute is not read-only.
1112           *
1113           * @param {String} attr The name of the attribute
1114           * @param {Object} cfg The attribute configuration object
1115           * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
1116           *
1117           * @return {Any} The initial value of the attribute.
1118           *
1119           * @method _getAttrInitVal
1120           * @private
1121           */
1122          _getAttrInitVal : function(attr, cfg, initValues) {
1123              var val = cfg.value,
1124                  valFn = cfg.valueFn,
1125                  tmpVal,
1126                  initValSet = false,
1127                  readOnly = cfg.readOnly,
1128                  simple,
1129                  complex,
1130                  i,
1131                  l,
1132                  path,
1133                  subval,
1134                  subvals;
1135  
1136              if (!readOnly && initValues) {
1137                  // Simple Attributes
1138                  simple = initValues.simple;
1139                  if (simple && simple.hasOwnProperty(attr)) {
1140                      val = simple[attr];
1141                      initValSet = true;
1142                  }
1143              }
1144  
1145              if (valFn && !initValSet) {
1146                  if (!valFn.call) {
1147                      valFn = this[valFn];
1148                  }
1149                  if (valFn) {
1150                      tmpVal = valFn.call(this, attr);
1151                      val = tmpVal;
1152                  }
1153              }
1154  
1155              if (!readOnly && initValues) {
1156  
1157                  // Complex Attributes (complex values applied, after simple, in case both are set)
1158                  complex = initValues.complex;
1159  
1160                  if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) {
1161                      subvals = complex[attr];
1162                      for (i = 0, l = subvals.length; i < l; ++i) {
1163                          path = subvals[i].path;
1164                          subval = subvals[i].value;
1165                          O.setValue(val, path, subval);
1166                      }
1167                  }
1168              }
1169  
1170              return val;
1171          },
1172  
1173          /**
1174           * Utility method to set up initial attributes defined during construction,
1175           * either through the constructor.ATTRS property, or explicitly passed in.
1176           *
1177           * @method _initAttrs
1178           * @protected
1179           * @param attrs {Object} The attributes to add during construction (passed through to <a href="#method_addAttrs">addAttrs</a>).
1180           *        These can also be defined on the constructor being augmented with Attribute by defining the ATTRS property on the constructor.
1181           * @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>).
1182           *        These are not merged/cloned. The caller is responsible for isolating user provided values if required.
1183           * @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
1184           */
1185          _initAttrs : function(attrs, values, lazy) {
1186              // ATTRS support for Node, which is not Base based
1187              attrs = attrs || this.constructor.ATTRS;
1188  
1189              var Base = Y.Base,
1190                  BaseCore = Y.BaseCore,
1191                  baseInst = (Base && Y.instanceOf(this, Base)),
1192                  baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore));
1193  
1194              if (attrs && !baseInst && !baseCoreInst) {
1195                  this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy);
1196              }
1197          }
1198      };
1199  
1200      Y.AttributeCore = AttributeCore;
1201  
1202  
1203  }, '3.17.2', {"requires": ["oop"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1