API Docs for: 3.8.0
Show:

File: base/js/BaseBuild.js

  1.     /**
  2.      * The base-build submodule provides Base.build functionality, which
  3.      * can be used to create custom classes, by aggregating extensions onto
  4.      * a main class.
  5.      *
  6.      * @module base
  7.      * @submodule base-build
  8.      * @for Base
  9.      */
  10.     var BaseCore = Y.BaseCore,
  11.         Base     = Y.Base,
  12.         L        = Y.Lang,

  13.         INITIALIZER = "initializer",
  14.         DESTRUCTOR  = "destructor",
  15.         AGGREGATES  = ["_PLUG", "_UNPLUG"],

  16.         build;

  17.     // Utility function used in `_buildCfg` to aggregate array values into a new
  18.     // array from the sender constructor to the reciver constructor.
  19.     function arrayAggregator(prop, r, s) {
  20.         if (s[prop]) {
  21.             r[prop] = (r[prop] || []).concat(s[prop]);
  22.         }
  23.     }

  24.     // Utility function used in `_buildCfg` to aggregate `_ATTR_CFG` array
  25.     // values from the sender constructor into a new array on reciver's
  26.     // constructor, and clear the cached hash.
  27.     function attrCfgAggregator(prop, r, s) {
  28.         if (s._ATTR_CFG) {
  29.             arrayAggregator.apply(null, arguments);

  30.             // Clear cached hash.
  31.             r._ATTR_CFG_HASH = null;
  32.         }
  33.     }

  34.     // Utility function used in `_buildCfg` to aggregate ATTRS configs from one
  35.     // the sender constructor to the reciver constructor.
  36.     function attrsAggregator(prop, r, s) {
  37.         var sAttrs, rAttrs, a;

  38.         r.ATTRS = r.ATTRS || {};

  39.         if (s.ATTRS) {
  40.             sAttrs = s.ATTRS;
  41.             rAttrs = r.ATTRS;

  42.             for (a in sAttrs) {
  43.                 if (sAttrs.hasOwnProperty(a)) {
  44.                     rAttrs[a] = rAttrs[a] || {};
  45.                     Y.mix(rAttrs[a], sAttrs[a], true);
  46.                 }
  47.             }
  48.         }
  49.     }

  50.     Base._build = function(name, main, extensions, px, sx, cfg) {

  51.         var build = Base._build,

  52.             builtClass = build._ctor(main, cfg),
  53.             buildCfg = build._cfg(main, cfg, extensions),

  54.             _mixCust = build._mixCust,

  55.             dynamic = builtClass._yuibuild.dynamic,

  56.             i, l, extClass, extProto,
  57.             initializer,
  58.             destructor;

  59.         // Augment/Aggregate
  60.         for (i = 0, l = extensions.length; i < l; i++) {
  61.             extClass = extensions[i];

  62.             extProto = extClass.prototype;

  63.             initializer = extProto[INITIALIZER];
  64.             destructor = extProto[DESTRUCTOR];
  65.             delete extProto[INITIALIZER];
  66.             delete extProto[DESTRUCTOR];

  67.             // Prototype, old non-displacing augment
  68.             Y.mix(builtClass, extClass, true, null, 1);

  69.             // Custom Statics
  70.             _mixCust(builtClass, extClass, buildCfg);

  71.             if (initializer) {
  72.                 extProto[INITIALIZER] = initializer;
  73.             }

  74.             if (destructor) {
  75.                 extProto[DESTRUCTOR] = destructor;
  76.             }

  77.             builtClass._yuibuild.exts.push(extClass);
  78.         }

  79.         if (px) {
  80.             Y.mix(builtClass.prototype, px, true);
  81.         }

  82.         if (sx) {
  83.             Y.mix(builtClass, build._clean(sx, buildCfg), true);
  84.             _mixCust(builtClass, sx, buildCfg);
  85.         }

  86.         builtClass.prototype.hasImpl = build._impl;

  87.         if (dynamic) {
  88.             builtClass.NAME = name;
  89.             builtClass.prototype.constructor = builtClass;
  90.         }

  91.         return builtClass;
  92.     };

  93.     build = Base._build;

  94.     Y.mix(build, {

  95.         _mixCust: function(r, s, cfg) {

  96.             var aggregates,
  97.                 custom,
  98.                 statics,
  99.                 aggr,
  100.                 l,
  101.                 i;

  102.             if (cfg) {
  103.                 aggregates = cfg.aggregates;
  104.                 custom = cfg.custom;
  105.                 statics = cfg.statics;
  106.             }

  107.             if (statics) {
  108.                 Y.mix(r, s, true, statics);
  109.             }

  110.             if (aggregates) {
  111.                 for (i = 0, l = aggregates.length; i < l; i++) {
  112.                     aggr = aggregates[i];
  113.                     if (!r.hasOwnProperty(aggr) && s.hasOwnProperty(aggr)) {
  114.                         r[aggr] = L.isArray(s[aggr]) ? [] : {};
  115.                     }
  116.                     Y.aggregate(r, s, true, [aggr]);
  117.                 }
  118.             }

  119.             if (custom) {
  120.                 for (i in custom) {
  121.                     if (custom.hasOwnProperty(i)) {
  122.                         custom[i](i, r, s);
  123.                     }
  124.                 }
  125.             }

  126.         },

  127.         _tmpl: function(main) {

  128.             function BuiltClass() {
  129.                 BuiltClass.superclass.constructor.apply(this, arguments);
  130.             }
  131.             Y.extend(BuiltClass, main);

  132.             return BuiltClass;
  133.         },

  134.         _impl : function(extClass) {
  135.             var classes = this._getClasses(), i, l, cls, exts, ll, j;
  136.             for (i = 0, l = classes.length; i < l; i++) {
  137.                 cls = classes[i];
  138.                 if (cls._yuibuild) {
  139.                     exts = cls._yuibuild.exts;
  140.                     ll = exts.length;

  141.                     for (j = 0; j < ll; j++) {
  142.                         if (exts[j] === extClass) {
  143.                             return true;
  144.                         }
  145.                     }
  146.                 }
  147.             }
  148.             return false;
  149.         },

  150.         _ctor : function(main, cfg) {

  151.            var dynamic = (cfg && false === cfg.dynamic) ? false : true,
  152.                builtClass = (dynamic) ? build._tmpl(main) : main,
  153.                buildCfg = builtClass._yuibuild;

  154.             if (!buildCfg) {
  155.                 buildCfg = builtClass._yuibuild = {};
  156.             }

  157.             buildCfg.id = buildCfg.id || null;
  158.             buildCfg.exts = buildCfg.exts || [];
  159.             buildCfg.dynamic = dynamic;

  160.             return builtClass;
  161.         },

  162.         _cfg : function(main, cfg, exts) {
  163.             var aggr = [],
  164.                 cust = {},
  165.                 statics = [],
  166.                 buildCfg,
  167.                 cfgAggr = (cfg && cfg.aggregates),
  168.                 cfgCustBuild = (cfg && cfg.custom),
  169.                 cfgStatics = (cfg && cfg.statics),
  170.                 c = main,
  171.                 i,
  172.                 l;

  173.             // Prototype Chain
  174.             while (c && c.prototype) {
  175.                 buildCfg = c._buildCfg;
  176.                 if (buildCfg) {
  177.                     if (buildCfg.aggregates) {
  178.                         aggr = aggr.concat(buildCfg.aggregates);
  179.                     }
  180.                     if (buildCfg.custom) {
  181.                         Y.mix(cust, buildCfg.custom, true);
  182.                     }
  183.                     if (buildCfg.statics) {
  184.                         statics = statics.concat(buildCfg.statics);
  185.                     }
  186.                 }
  187.                 c = c.superclass ? c.superclass.constructor : null;
  188.             }

  189.             // Exts
  190.             if (exts) {
  191.                 for (i = 0, l = exts.length; i < l; i++) {
  192.                     c = exts[i];
  193.                     buildCfg = c._buildCfg;
  194.                     if (buildCfg) {
  195.                         if (buildCfg.aggregates) {
  196.                             aggr = aggr.concat(buildCfg.aggregates);
  197.                         }
  198.                         if (buildCfg.custom) {
  199.                             Y.mix(cust, buildCfg.custom, true);
  200.                         }
  201.                         if (buildCfg.statics) {
  202.                             statics = statics.concat(buildCfg.statics);
  203.                         }
  204.                     }
  205.                 }
  206.             }

  207.             if (cfgAggr) {
  208.                 aggr = aggr.concat(cfgAggr);
  209.             }

  210.             if (cfgCustBuild) {
  211.                 Y.mix(cust, cfg.cfgBuild, true);
  212.             }

  213.             if (cfgStatics) {
  214.                 statics = statics.concat(cfgStatics);
  215.             }

  216.             return {
  217.                 aggregates: aggr,
  218.                 custom: cust,
  219.                 statics: statics
  220.             };
  221.         },

  222.         _clean : function(sx, cfg) {
  223.             var prop, i, l, sxclone = Y.merge(sx),
  224.                 aggregates = cfg.aggregates,
  225.                 custom = cfg.custom;

  226.             for (prop in custom) {
  227.                 if (sxclone.hasOwnProperty(prop)) {
  228.                     delete sxclone[prop];
  229.                 }
  230.             }

  231.             for (i = 0, l = aggregates.length; i < l; i++) {
  232.                 prop = aggregates[i];
  233.                 if (sxclone.hasOwnProperty(prop)) {
  234.                     delete sxclone[prop];
  235.                 }
  236.             }

  237.             return sxclone;
  238.         }
  239.     });

  240.     /**
  241.      * <p>
  242.      * Builds a custom constructor function (class) from the
  243.      * main function, and array of extension functions (classes)
  244.      * provided. The NAME field for the constructor function is
  245.      * defined by the first argument passed in.
  246.      * </p>
  247.      * <p>
  248.      * The cfg object supports the following properties
  249.      * </p>
  250.      * <dl>
  251.      *    <dt>dynamic &#60;boolean&#62;</dt>
  252.      *    <dd>
  253.      *    <p>If true (default), a completely new class
  254.      *    is created which extends the main class, and acts as the
  255.      *    host on which the extension classes are augmented.</p>
  256.      *    <p>If false, the extensions classes are augmented directly to
  257.      *    the main class, modifying the main class' prototype.</p>
  258.      *    </dd>
  259.      *    <dt>aggregates &#60;String[]&#62;</dt>
  260.      *    <dd>An array of static property names, which will get aggregated
  261.      *    on to the built class, in addition to the default properties build
  262.      *    will always aggregate as defined by the main class' static _buildCfg
  263.      *    property.
  264.      *    </dd>
  265.      * </dl>
  266.      *
  267.      * @method build
  268.      * @deprecated Use the more convenient Base.create and Base.mix methods instead
  269.      * @static
  270.      * @param {Function} name The name of the new class. Used to define the NAME property for the new class.
  271.      * @param {Function} main The main class on which to base the built class
  272.      * @param {Function[]} extensions The set of extension classes which will be
  273.      * augmented/aggregated to the built class.
  274.      * @param {Object} cfg Optional. Build configuration for the class (see description).
  275.      * @return {Function} A custom class, created from the provided main and extension classes
  276.      */
  277.     Base.build = function(name, main, extensions, cfg) {
  278.         return build(name, main, extensions, null, null, cfg);
  279.     };

  280.     /**
  281.      * Creates a new class (constructor function) which extends the base class passed in as the second argument,
  282.      * and mixes in the array of extensions provided.
  283.      *
  284.      * Prototype properties or methods can be added to the new class, using the px argument (similar to Y.extend).
  285.      *
  286.      * Static properties or methods can be added to the new class, using the sx argument (similar to Y.extend).
  287.      *
  288.      * **NOTE FOR COMPONENT DEVELOPERS**: Both the `base` class, and `extensions` can define static a `_buildCfg`
  289.      * property, which acts as class creation meta-data, and drives how special static properties from the base
  290.      * class, or extensions should be copied, aggregated or (custom) mixed into the newly created class.
  291.      *
  292.      * The `_buildCfg` property is a hash with 3 supported properties: `statics`, `aggregates` and `custom`, e.g:
  293.      *
  294.      *     // If the Base/Main class is the thing introducing the property:
  295.      *
  296.      *     MyBaseClass._buildCfg = {
  297.      *
  298.      *        // Static properties/methods to copy (Alias) to the built class.
  299.      *        statics: ["CopyThisMethod", "CopyThisProperty"],
  300.      *
  301.      *        // Static props to aggregate onto the built class.
  302.      *        aggregates: ["AggregateThisProperty"],
  303.      *
  304.      *        // Static properties which need custom handling (e.g. deep merge etc.)
  305.      *        custom: {
  306.      *           "CustomProperty" : function(property, Receiver, Supplier) {
  307.      *              ...
  308.      *              var triggers = Receiver.CustomProperty.triggers;
  309.                     Receiver.CustomProperty.triggers = triggers.concat(Supplier.CustomProperty.triggers);
  310.      *              ...
  311.      *           }
  312.      *        }
  313.      *     };
  314.      *
  315.      *     MyBaseClass.CopyThisMethod = function() {...};
  316.      *     MyBaseClass.CopyThisProperty = "foo";
  317.      *     MyBaseClass.AggregateThisProperty = {...};
  318.      *     MyBaseClass.CustomProperty = {
  319.      *        triggers: [...]
  320.      *     }
  321.      *
  322.      *     // Or, if the Extension is the thing introducing the property:
  323.      *
  324.      *     MyExtension._buildCfg = {
  325.      *         statics : ...
  326.      *         aggregates : ...
  327.      *         custom : ...
  328.      *     }
  329.      *
  330.      * This way, when users pass your base or extension class to `Y.Base.create` or `Y.Base.mix`, they don't need to
  331.      * know which properties need special handling. `Y.Base` has a buildCfg which defines `ATTRS` for custom mix handling
  332.      * (to protect the static config objects), and `Y.Widget` has a buildCfg which specifies `HTML_PARSER` for
  333.      * straight up aggregation.
  334.      *
  335.      * @method create
  336.      * @static
  337.      * @param {Function} name The name of the newly created class. Used to define the NAME property for the new class.
  338.      * @param {Function} main The base class which the new class should extend. This class needs to be Base or a class derived from base (e.g. Widget).
  339.      * @param {Function[]} extensions The list of extensions which will be mixed into the built class.
  340.      * @param {Object} px The set of prototype properties/methods to add to the built class.
  341.      * @param {Object} sx The set of static properties/methods to add to the built class.
  342.      * @return {Function} The newly created class.
  343.      */
  344.     Base.create = function(name, base, extensions, px, sx) {
  345.         return build(name, base, extensions, px, sx);
  346.     };

  347.     /**
  348.      * <p>Mixes in a list of extensions to an existing class.</p>
  349.      * @method mix
  350.      * @static
  351.      * @param {Function} main The existing class into which the extensions should be mixed.  The class needs to be Base or a class derived from Base (e.g. Widget)
  352.      * @param {Function[]} extensions The set of extension classes which will mixed into the existing main class.
  353.      * @return {Function} The modified main class, with extensions mixed in.
  354.      */
  355.     Base.mix = function(main, extensions) {
  356.         return build(null, main, extensions, null, null, {dynamic:false});
  357.     };

  358.     /**
  359.      * The build configuration for the Base class.
  360.      *
  361.      * Defines the static fields which need to be aggregated when the Base class
  362.      * is used as the main class passed to the
  363.      * <a href="#method_Base.build">Base.build</a> method.
  364.      *
  365.      * @property _buildCfg
  366.      * @type Object
  367.      * @static
  368.      * @final
  369.      * @private
  370.      */
  371.     BaseCore._buildCfg = {
  372.         custom: {
  373.             ATTRS         : attrsAggregator,
  374.             _ATTR_CFG     : attrCfgAggregator,
  375.             _NON_ATTRS_CFG: arrayAggregator
  376.         },

  377.         aggregates: AGGREGATES.concat()
  378.     };

  379.     // Makes sure Base and BaseCore use separate `_buildCfg` objects.
  380.     Base._buildCfg = {
  381.         custom: {
  382.             ATTRS         : attrsAggregator,
  383.             _ATTR_CFG     : attrCfgAggregator,
  384.             _NON_ATTRS_CFG: arrayAggregator
  385.         },

  386.         aggregates: AGGREGATES.concat()
  387.     };

  388.