[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/attribute-core/ -> attribute-core-debug.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              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"]});


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