text: /**
   * @ignore
   * attribute management
   * @author yiminghe@gmail.com, lifesinger@gmail.com
   */
  KISSY.add('base/attribute', function (S, EventCustom, undefined) {
  
      // atomic flag
      Attribute.INVALID = {};
  
      var INVALID = Attribute.INVALID;
  
      var FALSE = false;
  
      function normalFn(host, method) {
          if (typeof method == 'string') {
              return host[method];
          }
          return method;
      }
  
      function whenAttrChangeEventName(when, name) {
          return when + S.ucfirst(name) + 'Change';
      }
  
      // fire attribute value change
      function __fireAttrChange(self, when, name, prevVal, newVal, subAttrName, attrName, data) {
          attrName = attrName || name;
          return self.fire(whenAttrChangeEventName(when, name), S.mix({
              attrName: attrName,
              subAttrName: subAttrName,
              prevVal: prevVal,
              newVal: newVal
          }, data));
      }
  
      function ensureNonEmpty(obj, name, create) {
          var ret = obj[name] || {};
          if (create) {
              obj[name] = ret;
          }
          return ret;
      }
  
      function getAttrs(self) {
          /*
           attribute meta information
           {
           attrName: {
           getter: function,
           setter: function,
           // 注意：只能是普通对象以及系统内置类型，而不能是 new Xx()，否则用 valueFn 替代
           value: v, // default value
           valueFn: function
           }
           }
           */
          return ensureNonEmpty(self, '__attrs', true);
      }
  
  
      function getAttrVals(self) {
          /*
           attribute value
           {
           attrName: attrVal
           }
           */
          return ensureNonEmpty(self, '__attrVals', true);
      }
  
      /*
       o, [x,y,z] => o[x][y][z]
       */
      function getValueByPath(o, path) {
          for (var i = 0, len = path.length;
               o != undefined && i < len;
               i++) {
              o = o[path[i]];
          }
          return o;
      }
  
      /*
       o, [x,y,z], val => o[x][y][z]=val
       */
      function setValueByPath(o, path, val) {
          var len = path.length - 1,
              s = o;
          if (len >= 0) {
              for (var i = 0; i < len; i++) {
                  o = o[path[i]];
              }
              if (o != undefined) {
                  o[path[i]] = val;
              } else {
                  s = undefined;
              }
          }
          return s;
      }
  
      function getPathNamePair(name) {
          var path;
  
          if (name.indexOf('.') !== -1) {
              path = name.split('.');
              name = path.shift();
          }
  
          return {
              path: path,
              name: name
          };
      }
  
      function getValueBySubValue(prevVal, path, value) {
          var tmp = value;
          if (path) {
              if (prevVal === undefined) {
                  tmp = {};
              } else {
                  tmp = S.clone(prevVal);
              }
              setValueByPath(tmp, path, value);
          }
          return tmp;
      }
  
      function prepareDefaultSetFn(self, name) {
          var beforeChangeEventName = whenAttrChangeEventName('before', name),
              customEvent = EventCustom.getCustomEvent(self, beforeChangeEventName, 1);
          if (!customEvent.defaultFn) {
              customEvent.defaultFn = defaultSetFn;
          }
      }
  
      function setInternal(self, name, value, opts, attrs) {
          var path,
              subVal,
              prevVal,
              pathNamePair = getPathNamePair(name),
              fullName = name;
  
          name = pathNamePair.name;
          path = pathNamePair.path;
          prevVal = self.get(name);
  
          prepareDefaultSetFn(self, name);
  
          if (path) {
              subVal = getValueByPath(prevVal, path);
          }
  
          // if no change, just return
          if (!path && prevVal === value) {
              return undefined;
          } else if (path && subVal === value) {
              return undefined;
          }
  
          value = getValueBySubValue(prevVal, path, value);
  
          var beforeEventObject = S.mix({
              attrName: name,
              subAttrName: fullName,
              prevVal: prevVal,
              newVal: value,
              _opts: opts,
              _attrs: attrs,
              target: self
          }, opts.data);
  
          // check before event
          if (opts['silent']) {
              if (FALSE === defaultSetFn.call(self, beforeEventObject)) {
                  return FALSE;
              }
          } else {
              if (FALSE === self.fire(whenAttrChangeEventName('before', name), beforeEventObject)) {
                  return FALSE;
              }
          }
  
          return self;
      }
  
      function defaultSetFn(e) {
          // only consider itself, not bubbling!
          if (e.target !== this) {
              return;
          }
          var self = this,
              value = e.newVal,
              prevVal = e.prevVal,
              name = e.attrName,
              fullName = e.subAttrName,
              attrs = e._attrs,
              opts = e._opts;
  
          // set it
          var ret = self.setInternal(name, value);
  
          if (ret === FALSE) {
              return ret;
          }
  
          // fire after event
          if (!opts['silent']) {
              value = getAttrVals(self)[name];
              __fireAttrChange(self, 'after', name, prevVal, value, fullName, null, opts.data);
              if (attrs) {
                  attrs.push({
                      prevVal: prevVal,
                      newVal: value,
                      attrName: name,
                      subAttrName: fullName
                  });
              } else {
                  __fireAttrChange(self,
                      '', '*',
                      [prevVal], [value],
                      [fullName], [name],
                      opts.data);
              }
          }
  
          return undefined;
      }
  
      /**
       * @class KISSY.Base.Attribute
       * @private
       * Attribute provides configurable attribute support along with attribute change events.
       * It is designed to be augmented on to a host class,
       * and provides the host with the ability to configure attributes to store and retrieve state,
       * along with attribute change events.
       *
       * For example, attributes added to the host can be configured:
       *
       *  - With a setter function, which can be used to manipulate
       *  values passed to attribute 's {@link #set} method, before they are stored.
       *  - With a getter function, which can be used to manipulate stored values,
       *  before they are returned by attribute 's {@link #get} method.
       *  - With a validator function, to validate values before they are stored.
       *
       * See the {@link #addAttr} method, for the complete set of configuration
       * options available for attributes.
       *
       * NOTE: Most implementations will be better off extending the {@link KISSY.Base} class,
       * instead of augmenting Attribute directly.
       * Base augments Attribute and will handle the initial configuration
       * of attributes for derived classes, accounting for values passed into the constructor.
       */
      function Attribute() {
      }
  
      // for S.augment, no need to specify constructor
      Attribute.prototype = {
  
          /**
           * get un-cloned attr config collections
           * @return {Object}
           * @private
           */
          getAttrs: function () {
              return getAttrs(this);
          },
  
          /**
           * get un-cloned attr value collections
           * @return {Object}
           */
          getAttrVals: function () {
              var self = this,
                  o = {},
                  a,
                  attrs = getAttrs(self);
              for (a in attrs) {
                  o[a] = self.get(a);
              }
              return o;
          },
  
          /**
           * Adds an attribute with the provided configuration to the host object.
           * @param {String} name attrName
           * @param {Object} attrConfig The config supports the following properties
           * @param [attrConfig.value] simple object or system native object
           * @param [attrConfig.valueFn] a function which can return current attribute 's default value
           * @param {Function} [attrConfig.setter] call when set attribute 's value
           * pass current attribute 's value as parameter
           * if return value is not undefined,set returned value as real value
           * @param {Function} [attrConfig.getter] call when get attribute 's value
           * pass current attribute 's value as parameter
           * return getter's returned value to invoker
           * @param {Function} [attrConfig.validator]  call before set attribute 's value
           * if return false,cancel this set action
           * @param {Boolean} [override] whether override existing attribute config ,default true
           * @chainable
           */
          addAttr: function (name, attrConfig, override) {
              var self = this,
                  attrs = getAttrs(self),
                  attr,
                  cfg = S.clone(attrConfig);
              if (attr = attrs[name]) {
                  S.mix(attr, cfg, override);
              } else {
                  attrs[name] = cfg;
              }
              return self;
          },
  
          /**
           * Configures a group of attributes, and sets initial values.
           * @param {Object} attrConfigs  An object with attribute name/configuration pairs.
           * @param {Object} initialValues user defined initial values
           * @chainable
           */
          addAttrs: function (attrConfigs, initialValues) {
              var self = this;
              S.each(attrConfigs, function (attrConfig, name) {
                  self.addAttr(name, attrConfig);
              });
              if (initialValues) {
                  self.set(initialValues);
              }
              return self;
          },
  
          /**
           * Checks if the given attribute has been added to the host.
           * @param {String} name attribute name
           * @return {Boolean}
           */
          hasAttr: function (name) {
              return getAttrs(this).hasOwnProperty(name);
          },
  
          /**
           * Removes an attribute from the host object.
           * @chainable
           */
          removeAttr: function (name) {
              var self = this;
  
              if (self.hasAttr(name)) {
                  delete getAttrs(self)[name];
                  delete getAttrVals(self)[name];
              }
  
              return self;
          },
  
  
          /**
           * Sets the value of an attribute.
           * @param {String|Object} name attribute 's name or attribute name and value map
           * @param [value] attribute 's value
           * @param {Object} [opts] some options
           * @param {Boolean} [opts.silent] whether fire change event
           * @param {Function} [opts.error] error handler
           * @return {Boolean} whether pass validator
           */
          set: function (name, value, opts) {
              var self = this;
              if (S.isPlainObject(name)) {
                  opts = value;
                  opts = opts || {};
                  var all = Object(name),
                      attrs = [],
                      e,
                      errors = [];
                  for (name in all) {
                      // bulk validation
                      // if any one failed,all values are not set
                      if ((e = validate(self, name, all[name], all)) !== undefined) {
                          errors.push(e);
                      }
                  }
                  if (errors.length) {
                      if (opts['error']) {
                          opts['error'](errors);
                      }
                      return FALSE;
                  }
                  for (name in all) {
                      setInternal(self, name, all[name], opts, attrs);
                  }
                  var attrNames = [],
                      prevVals = [],
                      newVals = [],
                      subAttrNames = [];
                  S.each(attrs, function (attr) {
                      prevVals.push(attr.prevVal);
                      newVals.push(attr.newVal);
                      attrNames.push(attr.attrName);
                      subAttrNames.push(attr.subAttrName);
                  });
                  if (attrNames.length) {
                      __fireAttrChange(self,
                          '',
                          '*',
                          prevVals,
                          newVals,
                          subAttrNames,
                          attrNames,
                          opts.data);
                  }
                  return self;
              }
              opts = opts || {};
              // validator check
              e = validate(self, name, value);
  
              if (e !== undefined) {
                  if (opts['error']) {
                      opts['error'](e);
                  }
                  return FALSE;
              }
              return setInternal(self, name, value, opts);
          },
  
          /**
           * internal use, no event involved, just set.
           * @protected
           */
          setInternal: function (name, value) {
              var self = this,
                  setValue = undefined,
              // if host does not have meta info corresponding to (name,value)
              // then register on demand in order to collect all data meta info
              // 一定要注册属性元数据，否则其他模块通过 _attrs 不能枚举到所有有效属性
              // 因为属性在声明注册前可以直接设置值
                  attrConfig = ensureNonEmpty(getAttrs(self), name, true),
                  setter = attrConfig['setter'];
  
              // if setter has effect
              if (setter && (setter = normalFn(self, setter))) {
                  setValue = setter.call(self, value, name);
              }
  
              if (setValue === INVALID) {
                  return FALSE;
              }
  
              if (setValue !== undefined) {
                  value = setValue;
              }
  
              // finally set
              getAttrVals(self)[name] = value;
  
              return undefined;
          },
  
          /**
           * Gets the current value of the attribute.
           * @param {String} name attribute 's name
           * @return {*}
           */
          get: function (name) {
              var self = this,
                  dot = '.',
                  path,
                  attrVals = getAttrVals(self),
                  attrConfig,
                  getter, ret;
  
              if (name.indexOf(dot) !== -1) {
                  path = name.split(dot);
                  name = path.shift();
              }
  
              attrConfig = ensureNonEmpty(getAttrs(self), name);
              getter = attrConfig['getter'];
  
              // get user-set value or default value
              //user-set value takes privilege
              ret = name in attrVals ?
                  attrVals[name] :
                  getDefAttrVal(self, name);
  
              // invoke getter for this attribute
              if (getter && (getter = normalFn(self, getter))) {
                  ret = getter.call(self, ret, name);
              }
  
              if (!(name in attrVals) && ret !== undefined) {
                  attrVals[name] = ret;
              }
  
              if (path) {
                  ret = getValueByPath(ret, path);
              }
  
              return ret;
          },
  
          /**
           * Resets the value of an attribute.just reset what addAttr set
           * (not what invoker set when call new Xx(cfg))
           * @param {String} name name of attribute
           * @param {Object} [opts] some options
           * @param {Boolean} [opts.silent] whether fire change event
           * @chainable
           */
          reset: function (name, opts) {
              var self = this;
  
              if (typeof name == 'string') {
                  if (self.hasAttr(name)) {
                      // if attribute does not have default value, then set to undefined
                      return self.set(name, getDefAttrVal(self, name), opts);
                  }
                  else {
                      return self;
                  }
              }
  
              opts = /**@type Object
               @ignore*/(name);
  
              var attrs = getAttrs(self),
                  values = {};
  
              // reset all
              for (name in attrs) {
                  values[name] = getDefAttrVal(self, name);
              }
  
              self.set(values, opts);
              return self;
          }
      };
  
  
      // get default attribute value from valueFn/value
      function getDefAttrVal(self, name) {
          var attrs = getAttrs(self),
              attrConfig = ensureNonEmpty(attrs, name),
              valFn = attrConfig.valueFn,
              val;
  
          if (valFn && (valFn = normalFn(self, valFn))) {
              val = valFn.call(self);
              if (val !== undefined) {
                  attrConfig.value = val;
              }
              delete attrConfig.valueFn;
              attrs[name] = attrConfig;
          }
  
          return attrConfig.value;
      }
  
      function validate(self, name, value, all) {
          var path, prevVal, pathNamePair;
  
          pathNamePair = getPathNamePair(name);
  
          name = pathNamePair.name;
          path = pathNamePair.path;
  
          if (path) {
              prevVal = self.get(name);
              value = getValueBySubValue(prevVal, path, value);
          }
          var attrConfig = ensureNonEmpty(getAttrs(self), name, true),
              e,
              validator = attrConfig['validator'];
          if (validator && (validator = normalFn(self, validator))) {
              e = validator.call(self, value, name, all);
              // undefined and true validate successfully
              if (e !== undefined && e !== true) {
                  return e;
              }
          }
          return undefined;
      }
  
      return Attribute;
  }, {
      requires: ['event/custom']
  });
  
  /*
   2011-10-18
   get/set sub attribute value ,set('x.y',val) x 最好为 {} ，不要是 new Clz() 出来的
   add validator
   */
  