API Docs for: 3.8.0
Show:

File: base/js/BaseCore.js

  1.     /**
  2.      * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
  3.      * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides
  4.      * plugin support and also provides the BaseCore.build method which provides a way to build custom classes using extensions.
  5.      *
  6.      * @module base
  7.      */

  8.     /**
  9.      * <p>The base-core module provides the BaseCore class, the lightest version of Base,
  10.      * which provides Base's basic lifecycle management and ATTRS construction support,
  11.      * but doesn't fire init/destroy or attribute change events.</p>
  12.      *
  13.      * <p>It mixes in AttributeCore, which is the lightest version of Attribute</p>
  14.      *
  15.      * @module base
  16.      * @submodule base-core
  17.      */
  18.     var O = Y.Object,
  19.         L = Y.Lang,
  20.         DOT = ".",
  21.         INITIALIZED = "initialized",
  22.         DESTROYED = "destroyed",
  23.         INITIALIZER = "initializer",
  24.         VALUE = "value",
  25.         OBJECT_CONSTRUCTOR = Object.prototype.constructor,
  26.         DEEP = "deep",
  27.         SHALLOW = "shallow",
  28.         DESTRUCTOR = "destructor",

  29.         AttributeCore = Y.AttributeCore,

  30.         _wlmix = function(r, s, wlhash) {
  31.             var p;
  32.             for (p in s) {
  33.                 if(wlhash[p]) {
  34.                     r[p] = s[p];
  35.                 }
  36.             }
  37.             return r;
  38.         };

  39.     /**
  40.      * The BaseCore class, is the lightest version of Base, and provides Base's
  41.      * basic lifecycle management and ATTRS construction support, but doesn't
  42.      * fire init/destroy or attribute change events.
  43.      *
  44.      * BaseCore also handles the chaining of initializer and destructor methods across
  45.      * the hierarchy as part of object construction and destruction. Additionally, attributes
  46.      * configured through the static <a href="#property_BaseCore.ATTRS">ATTRS</a>
  47.      * property for each class in the hierarchy will be initialized by BaseCore.
  48.      *
  49.      * Classes which require attribute support, but don't intend to use/expose attribute
  50.      * change events can extend BaseCore instead of Base for optimal kweight and
  51.      * runtime performance.
  52.      *
  53.      * @class BaseCore
  54.      * @constructor
  55.      * @uses AttributeCore
  56.      * @param {Object} cfg Object with configuration property name/value pairs.
  57.      * The object can be used to provide initial values for the objects published
  58.      * attributes.
  59.      */
  60.     function BaseCore(cfg) {
  61.         if (!this._BaseInvoked) {
  62.             this._BaseInvoked = true;

  63.             Y.log('constructor called', 'life', 'base');
  64.             this._initBase(cfg);
  65.         }
  66.         else { Y.log('Based constructor called more than once. Ignoring duplicate calls', 'life', 'base'); }
  67.     }

  68.     /**
  69.      * The list of properties which can be configured for each attribute
  70.      * (e.g. setter, getter, writeOnce, readOnly etc.)
  71.      *
  72.      * @property _ATTR_CFG
  73.      * @type Array
  74.      * @static
  75.      * @private
  76.      */
  77.     BaseCore._ATTR_CFG = AttributeCore._ATTR_CFG.concat("cloneDefaultValue");

  78.     /**
  79.      * The array of non-attribute configuration properties supported by this class.
  80.      *
  81.      * For example `BaseCore` defines a "plugins" configuration property which
  82.      * should not be set up as an attribute. This property is primarily required so
  83.      * that when <a href="#property__allowAdHocAttrs">`_allowAdHocAttrs`</a> is enabled by a class,
  84.      * non-attribute configuration properties don't get added as ad-hoc attributes.
  85.      *
  86.      * @property _NON_ATTRS_CFG
  87.      * @type Array
  88.      * @static
  89.      * @private
  90.      */
  91.     BaseCore._NON_ATTRS_CFG = ["plugins"];

  92.     /**
  93.      * This property controls whether or not instances of this class should
  94.      * allow users to add ad-hoc attributes through the constructor configuration
  95.      * hash.
  96.      *
  97.      * AdHoc attributes are attributes which are not defined by the class, and are
  98.      * not handled by the MyClass._NON_ATTRS_CFG
  99.      *
  100.      * @property _allowAdHocAttrs
  101.      * @type boolean
  102.      * @default undefined (false)
  103.      * @protected
  104.      */

  105.     /**
  106.      * The string to be used to identify instances of this class.
  107.      *
  108.      * Classes extending BaseCore, should define their own
  109.      * static NAME property, which should be camelCase by
  110.      * convention (e.g. MyClass.NAME = "myClass";).
  111.      *
  112.      * @property NAME
  113.      * @type String
  114.      * @static
  115.      */
  116.     BaseCore.NAME = "baseCore";

  117.     /**
  118.      * The default set of attributes which will be available for instances of this class, and
  119.      * their configuration. In addition to the configuration properties listed by
  120.      * AttributeCore's <a href="AttributeCore.html#method_addAttr">addAttr</a> method,
  121.      * the attribute can also be configured with a "cloneDefaultValue" property, which
  122.      * defines how the statically defined value field should be protected
  123.      * ("shallow", "deep" and false are supported values).
  124.      *
  125.      * By default if the value is an object literal or an array it will be "shallow"
  126.      * cloned, to protect the default value.
  127.      *
  128.      * @property ATTRS
  129.      * @type Object
  130.      * @static
  131.      */
  132.     BaseCore.ATTRS = {
  133.         /**
  134.          * Flag indicating whether or not this object
  135.          * has been through the init lifecycle phase.
  136.          *
  137.          * @attribute initialized
  138.          * @readonly
  139.          * @default false
  140.          * @type boolean
  141.          */
  142.         initialized: {
  143.             readOnly:true,
  144.             value:false
  145.         },

  146.         /**
  147.          * Flag indicating whether or not this object
  148.          * has been through the destroy lifecycle phase.
  149.          *
  150.          * @attribute destroyed
  151.          * @readonly
  152.          * @default false
  153.          * @type boolean
  154.          */
  155.         destroyed: {
  156.             readOnly:true,
  157.             value:false
  158.         }
  159.     };

  160.     BaseCore.prototype = {

  161.         /**
  162.          * Internal construction logic for BaseCore.
  163.          *
  164.          * @method _initBase
  165.          * @param {Object} config The constructor configuration object
  166.          * @private
  167.          */
  168.         _initBase : function(config) {
  169.             Y.log('init called', 'life', 'base');

  170.             Y.stamp(this);

  171.             this._initAttribute(config);

  172.             // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
  173.             // initial state, but don't initialize Plugins yet. That's done after initialization.
  174.             var PluginHost = Y.Plugin && Y.Plugin.Host;
  175.             if (this._initPlugins && PluginHost) {
  176.                 PluginHost.call(this);
  177.             }

  178.             if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }

  179.             /**
  180.              * The string used to identify the class of this object.
  181.              *
  182.              * @deprecated Use this.constructor.NAME
  183.              * @property name
  184.              * @type String
  185.              */
  186.             this.name = this.constructor.NAME;

  187.             this.init.apply(this, arguments);
  188.         },

  189.         /**
  190.          * Initializes AttributeCore
  191.          *
  192.          * @method _initAttribute
  193.          * @private
  194.          */
  195.         _initAttribute: function() {
  196.             AttributeCore.call(this);
  197.         },

  198.         /**
  199.          * Init lifecycle method, invoked during construction. Sets up attributes
  200.          * and invokes initializers for the class hierarchy.
  201.          *
  202.          * @method init
  203.          * @chainable
  204.          * @param {Object} cfg Object with configuration property name/value pairs
  205.          * @return {BaseCore} A reference to this object
  206.          */
  207.         init: function(cfg) {
  208.             Y.log('init called', 'life', 'base');

  209.             this._baseInit(cfg);

  210.             return this;
  211.         },

  212.         /**
  213.          * Internal initialization implementation for BaseCore
  214.          *
  215.          * @method _baseInit
  216.          * @private
  217.          */
  218.         _baseInit: function(cfg) {
  219.             this._initHierarchy(cfg);

  220.             if (this._initPlugins) {
  221.                 // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
  222.                 this._initPlugins(cfg);
  223.             }
  224.             this._set(INITIALIZED, true);
  225.         },

  226.         /**
  227.          * Destroy lifecycle method. Invokes destructors for the class hierarchy.
  228.          *
  229.          * @method destroy
  230.          * @return {BaseCore} A reference to this object
  231.          * @chainable
  232.          */
  233.         destroy: function() {
  234.             this._baseDestroy();
  235.             return this;
  236.         },

  237.         /**
  238.          * Internal destroy implementation for BaseCore
  239.          *
  240.          * @method _baseDestroy
  241.          * @private
  242.          */
  243.         _baseDestroy : function() {
  244.             if (this._destroyPlugins) {
  245.                 this._destroyPlugins();
  246.             }
  247.             this._destroyHierarchy();
  248.             this._set(DESTROYED, true);
  249.         },

  250.         /**
  251.          * Returns the class hierarchy for this object, with BaseCore being the last class in the array.
  252.          *
  253.          * @method _getClasses
  254.          * @protected
  255.          * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
  256.          * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
  257.          * cached value.
  258.          */
  259.         _getClasses : function() {
  260.             if (!this._classes) {
  261.                 this._initHierarchyData();
  262.             }
  263.             return this._classes;
  264.         },

  265.         /**
  266.          * Returns an aggregated set of attribute configurations, by traversing
  267.          * the class hierarchy.
  268.          *
  269.          * @method _getAttrCfgs
  270.          * @protected
  271.          * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
  272.          * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
  273.          * the cached value.
  274.          */
  275.         _getAttrCfgs : function() {
  276.             if (!this._attrs) {
  277.                 this._initHierarchyData();
  278.             }
  279.             return this._attrs;
  280.         },

  281.         /**
  282.          * A helper method used when processing ATTRS across the class hierarchy during
  283.          * initialization. Returns a disposable object with the attributes defined for
  284.          * the provided class, extracted from the set of all attributes passed in.
  285.          *
  286.          * @method _filterAttrCfs
  287.          * @private
  288.          *
  289.          * @param {Function} clazz The class for which the desired attributes are required.
  290.          * @param {Object} allCfgs The set of all attribute configurations for this instance.
  291.          * Attributes will be removed from this set, if they belong to the filtered class, so
  292.          * that by the time all classes are processed, allCfgs will be empty.
  293.          *
  294.          * @return {Object} The set of attributes belonging to the class passed in, in the form
  295.          * of an object with attribute name/configuration pairs.
  296.          */
  297.         _filterAttrCfgs : function(clazz, allCfgs) {
  298.             var cfgs = null, attr, attrs = clazz.ATTRS;

  299.             if (attrs) {
  300.                 for (attr in attrs) {
  301.                     if (allCfgs[attr]) {
  302.                         cfgs = cfgs || {};
  303.                         cfgs[attr] = allCfgs[attr];
  304.                         allCfgs[attr] = null;
  305.                     }
  306.                 }
  307.             }

  308.             return cfgs;
  309.         },

  310.         /**
  311.          * @method _filterAdHocAttrs
  312.          * @private
  313.          *
  314.          * @param {Object} allAttrs The set of all attribute configurations for this instance.
  315.          * Attributes will be removed from this set, if they belong to the filtered class, so
  316.          * that by the time all classes are processed, allCfgs will be empty.
  317.          * @param {Object} userVals The config object passed in by the user, from which adhoc attrs are to be filtered.
  318.          * @return {Object} The set of adhoc attributes passed in, in the form
  319.          * of an object with attribute name/configuration pairs.
  320.          */
  321.         _filterAdHocAttrs : function(allAttrs, userVals) {
  322.             var adHocs,
  323.                 nonAttrs = this._nonAttrs,
  324.                 attr;

  325.             if (userVals) {
  326.                 adHocs = {};
  327.                 for (attr in userVals) {
  328.                     if (!allAttrs[attr] && !nonAttrs[attr] && userVals.hasOwnProperty(attr)) {
  329.                         adHocs[attr] = {
  330.                             value:userVals[attr]
  331.                         };
  332.                     }
  333.                 }
  334.             }

  335.             return adHocs;
  336.         },

  337.         /**
  338.          * A helper method used by _getClasses and _getAttrCfgs, which determines both
  339.          * the array of classes and aggregate set of attribute configurations
  340.          * across the class hierarchy for the instance.
  341.          *
  342.          * @method _initHierarchyData
  343.          * @private
  344.          */
  345.         _initHierarchyData : function() {
  346.             var ctor = this.constructor,
  347.                 c,
  348.                 i,
  349.                 l,
  350.                 attrCfg,
  351.                 attrCfgHash,
  352.                 needsAttrCfgHash = !ctor._ATTR_CFG_HASH,
  353.                 nonAttrsCfg,
  354.                 nonAttrs = (this._allowAdHocAttrs) ? {} : null,
  355.                 classes = [],
  356.                 attrs = [];

  357.             // Start with `this` instance's constructor.
  358.             c = ctor;

  359.             while (c) {
  360.                 // Add to classes
  361.                 classes[classes.length] = c;

  362.                 // Add to attributes
  363.                 if (c.ATTRS) {
  364.                     attrs[attrs.length] = c.ATTRS;
  365.                 }

  366.                 // Aggregate ATTR cfg whitelist.
  367.                 if (needsAttrCfgHash) {
  368.                     attrCfg     = c._ATTR_CFG;
  369.                     attrCfgHash = attrCfgHash || {};

  370.                     if (attrCfg) {
  371.                         for (i = 0, l = attrCfg.length; i < l; i += 1) {
  372.                             attrCfgHash[attrCfg[i]] = true;
  373.                         }
  374.                     }
  375.                 }

  376.                 if (this._allowAdHocAttrs) {
  377.                     nonAttrsCfg = c._NON_ATTRS_CFG;
  378.                     if (nonAttrsCfg) {
  379.                         for (i = 0, l = nonAttrsCfg.length; i < l; i++) {
  380.                             nonAttrs[nonAttrsCfg[i]] = true;
  381.                         }
  382.                     }
  383.                 }

  384.                 c = c.superclass ? c.superclass.constructor : null;
  385.             }

  386.             // Cache computed `_ATTR_CFG_HASH` on the constructor.
  387.             if (needsAttrCfgHash) {
  388.                 ctor._ATTR_CFG_HASH = attrCfgHash;
  389.             }

  390.             this._classes = classes;
  391.             this._nonAttrs = nonAttrs;
  392.             this._attrs = this._aggregateAttrs(attrs);
  393.         },

  394.         /**
  395.          * Utility method to define the attribute hash used to filter/whitelist property mixes for
  396.          * this class.
  397.          *
  398.          * @method _attrCfgHash
  399.          * @private
  400.          */
  401.         _attrCfgHash: function() {
  402.             return this.constructor._ATTR_CFG_HASH;
  403.         },

  404.         /**
  405.          * A helper method, used by _initHierarchyData to aggregate
  406.          * attribute configuration across the instances class hierarchy.
  407.          *
  408.          * The method will protect the attribute configuration value to protect the statically defined
  409.          * default value in ATTRS if required (if the value is an object literal, array or the
  410.          * attribute configuration has cloneDefaultValue set to shallow or deep).
  411.          *
  412.          * @method _aggregateAttrs
  413.          * @private
  414.          * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
  415.          * (subclass first, Base last)
  416.          * @return {Object} The aggregate set of ATTRS definitions for the instance
  417.          */
  418.         _aggregateAttrs : function(allAttrs) {
  419.             var attr,
  420.                 attrs,
  421.                 cfg,
  422.                 val,
  423.                 path,
  424.                 i,
  425.                 clone,
  426.                 cfgPropsHash = this._attrCfgHash(),
  427.                 aggAttr,
  428.                 aggAttrs = {};

  429.             if (allAttrs) {
  430.                 for (i = allAttrs.length-1; i >= 0; --i) {
  431.                     attrs = allAttrs[i];

  432.                     for (attr in attrs) {
  433.                         if (attrs.hasOwnProperty(attr)) {

  434.                             // Protect config passed in
  435.                             cfg = _wlmix({}, attrs[attr], cfgPropsHash);

  436.                             val = cfg.value;
  437.                             clone = cfg.cloneDefaultValue;

  438.                             if (val) {
  439.                                 if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
  440.                                     Y.log('Cloning default value for attribute:' + attr, 'info', 'base');
  441.                                     cfg.value = Y.clone(val);
  442.                                 } else if (clone === SHALLOW) {
  443.                                     Y.log('Merging default value for attribute:' + attr, 'info', 'base');
  444.                                     cfg.value = Y.merge(val);
  445.                                 }
  446.                                 // else if (clone === false), don't clone the static default value.
  447.                                 // It's intended to be used by reference.
  448.                             }

  449.                             path = null;
  450.                             if (attr.indexOf(DOT) !== -1) {
  451.                                 path = attr.split(DOT);
  452.                                 attr = path.shift();
  453.                             }

  454.                             aggAttr = aggAttrs[attr];
  455.                             if (path && aggAttr && aggAttr.value) {
  456.                                 O.setValue(aggAttr.value, path, val);
  457.                             } else if (!path) {
  458.                                 if (!aggAttr) {
  459.                                     aggAttrs[attr] = cfg;
  460.                                 } else {
  461.                                     if (aggAttr.valueFn && VALUE in cfg) {
  462.                                         aggAttr.valueFn = null;
  463.                                     }
  464.                                     _wlmix(aggAttr, cfg, cfgPropsHash);
  465.                                 }
  466.                             }
  467.                         }
  468.                     }
  469.                 }
  470.             }

  471.             return aggAttrs;
  472.         },

  473.         /**
  474.          * Initializes the class hierarchy for the instance, which includes
  475.          * initializing attributes for each class defined in the class's
  476.          * static <a href="#property_BaseCore.ATTRS">ATTRS</a> property and
  477.          * invoking the initializer method on the prototype of each class in the hierarchy.
  478.          *
  479.          * @method _initHierarchy
  480.          * @param {Object} userVals Object with configuration property name/value pairs
  481.          * @private
  482.          */
  483.         _initHierarchy : function(userVals) {
  484.             var lazy = this._lazyAddAttrs,
  485.                 constr,
  486.                 constrProto,
  487.                 ci,
  488.                 ei,
  489.                 el,
  490.                 extProto,
  491.                 exts,
  492.                 classes = this._getClasses(),
  493.                 attrCfgs = this._getAttrCfgs(),
  494.                 cl = classes.length - 1;

  495.             for (ci = cl; ci >= 0; ci--) {

  496.                 constr = classes[ci];
  497.                 constrProto = constr.prototype;
  498.                 exts = constr._yuibuild && constr._yuibuild.exts;

  499.                 if (exts) {
  500.                     for (ei = 0, el = exts.length; ei < el; ei++) {
  501.                         exts[ei].apply(this, arguments);
  502.                     }
  503.                 }

  504.                 this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);

  505.                 if (this._allowAdHocAttrs && ci === cl) {
  506.                     this.addAttrs(this._filterAdHocAttrs(attrCfgs, userVals), userVals, lazy);
  507.                 }

  508.                 // Using INITIALIZER in hasOwnProperty check, for performance reasons (helps IE6 avoid GC thresholds when
  509.                 // referencing string literals). Not using it in apply, again, for performance "." is faster.
  510.                 if (constrProto.hasOwnProperty(INITIALIZER)) {
  511.                     constrProto.initializer.apply(this, arguments);
  512.                 }

  513.                 if (exts) {
  514.                     for (ei = 0; ei < el; ei++) {
  515.                         extProto = exts[ei].prototype;
  516.                         if (extProto.hasOwnProperty(INITIALIZER)) {
  517.                             extProto.initializer.apply(this, arguments);
  518.                         }
  519.                     }
  520.                 }
  521.             }
  522.         },

  523.         /**
  524.          * Destroys the class hierarchy for this instance by invoking
  525.          * the destructor method on the prototype of each class in the hierarchy.
  526.          *
  527.          * @method _destroyHierarchy
  528.          * @private
  529.          */
  530.         _destroyHierarchy : function() {
  531.             var constr,
  532.                 constrProto,
  533.                 ci, cl, ei, el, exts, extProto,
  534.                 classes = this._getClasses();

  535.             for (ci = 0, cl = classes.length; ci < cl; ci++) {
  536.                 constr = classes[ci];
  537.                 constrProto = constr.prototype;
  538.                 exts = constr._yuibuild && constr._yuibuild.exts;

  539.                 if (exts) {
  540.                     for (ei = 0, el = exts.length; ei < el; ei++) {
  541.                         extProto = exts[ei].prototype;
  542.                         if (extProto.hasOwnProperty(DESTRUCTOR)) {
  543.                             extProto.destructor.apply(this, arguments);
  544.                         }
  545.                     }
  546.                 }

  547.                 if (constrProto.hasOwnProperty(DESTRUCTOR)) {
  548.                     constrProto.destructor.apply(this, arguments);
  549.                 }
  550.             }
  551.         },

  552.         /**
  553.          * Default toString implementation. Provides the constructor NAME
  554.          * and the instance guid, if set.
  555.          *
  556.          * @method toString
  557.          * @return {String} String representation for this object
  558.          */
  559.         toString: function() {
  560.             return this.name + "[" + Y.stamp(this, true) + "]";
  561.         }
  562.     };

  563.     // Straightup augment, no wrapper functions
  564.     Y.mix(BaseCore, AttributeCore, false, null, 1);

  565.     // Fix constructor
  566.     BaseCore.prototype.constructor = BaseCore;

  567.     Y.BaseCore = BaseCore;

  568.