index.mustache revision e808b8824ca1091c8efb5669db9129e68e5e1c14
fb60765e69976e9df4bd2e9432d7af8f4297888bMark Andrews<div class="intro">
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews <p>Base is designed to be a low-level foundation class from which other attribute- and event target-based classes in the
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence YUI library can be derived. It provides a standard template for creating attribute-based objects across the
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater library and provides a consistent `init()` and `destroy()` sequence that chains
4c1132f34493327abc632196f5876a89aa573687Bob Halley initialization (`initializer`) and destruction (`destructor`) methods for the class hierarchy.</p>
4c1132f34493327abc632196f5876a89aa573687Bob Halley
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence <p>It also provides a way for classes to reuse implementation code through plugins, or through extensions.</p>
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews</div>
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews{{>getting-started}}
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews<h2 id="extendbase">Extending Base</h2>
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews<p>Although Base can be instantiated, it's really designed to be a root class, which you extend when creating your
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrewsown `Attribute` and `EventTarget` based classes as shown below:</p>
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence```
f41f183f628a148860a6d1f0070208cddd45b0c6Bob HalleyYUI().use("base", function(Y) {
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley function MyClass(config) {
f41f183f628a148860a6d1f0070208cddd45b0c6Bob Halley
d0ad0044249ab08201ce8a1029253f2c6ef41147Bob Halley // Invoke Base constructor, passing through arguments
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley MyClass.superclass.constructor.apply(this, arguments);
9ed37e8b9ccd53bc37b546fffe487b9547dda3a0Mark Andrews }
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley Y.extend(MyClass, Y.Base, {
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley // Prototype methods for your new class
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley });
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews});
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews```
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews<p>Base itself augments `Attribute`, which in turn augments `EventTarget`; Base is therefore both an <em>Attribute provider</em> and
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrewsan <em>Event Target</em>.</p>
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley<p>Base's constructor expects a configuration object literal, which is used to set up initial values for attributes during construction (discussed below).</p>
2f2e3e1c38aabeef784566870d885adfa7f00a48David Lawrence
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews<h2 id="staticprops">Static Properties</h2>
9d3ad53203f1ac49f9e876dbbcaca18656eb3e19Mark Andrews
a5d43b72413db3edd6b36a58f9bdf2cf6ff692f2Bob Halley<p>Base looks for two "static" properties which it requires you to add to your class constructor &mdash; `NAME` and `ATTRS`. Base uses these properties when setting up events and attributes for the class:</p>
ca5cbad980fb78b3e79787bbdf049b1e5dfb3376Bob Halley
ca5cbad980fb78b3e79787bbdf049b1e5dfb3376Bob Halley```
ca5cbad980fb78b3e79787bbdf049b1e5dfb3376Bob Halleyfunction MyClass(config) {
ca5cbad980fb78b3e79787bbdf049b1e5dfb3376Bob Halley MyClass.superclass.constructor.apply(this, arguments);
ca5cbad980fb78b3e79787bbdf049b1e5dfb3376Bob Halley}
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas Gustafsson
7d7f929274e48808b4771162d6302a99e69865d8Mark Andrews// Used to identify instances of this class
7d7f929274e48808b4771162d6302a99e69865d8Mark Andrews// For example, to prefix event names
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas Gustafsson
280747fa7c1d4597d47f7be8ec5fb7c8980c1952Andreas GustafssonMyClass.NAME = "myClass";
5e88852b94830bf71e37dc700d568cb35e2e6f7eAndreas Gustafsson
3ee5e4d6a40fdc413c6216048e7c162eb5e6b295Brian Wellington// "Associative Array", used to define the set of attributes
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley// added by this class. The name of the attribute is the key,
93593fa445f77a5517103be29544f9ecef0e2d2dBob Halley// and the object literal value acts as the configuration
8e06cea14c857429ab7e7299af2dce5eeeaa5ff0Michael Graff// object passed to addAttrs
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas GustafssonMyClass.ATTRS = {
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson A : {
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson // Attribute "A" configuration
a0d172f105ac277a9e56e1b61e6863d279f2ff75Andreas Gustafsson },
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein B : {
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein // Attribute "B" configuration
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein }
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein}
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob AusteinY.extend(MyClass, Y.Base, {
94a8c22d559fe1f977ed41d6fe0b2513ecee7ff9Rob Austein // Prototype methods for your new class
});
```
<h3 id="nameprop">NAME</h3>
<p>The `NAME` property is a string which is used to identify the class.</p>
<p>One core area where it is currently used is to prefix all events that are published by instances of your class.
For example, any events published by the class `MyClass` in the above code snippet will have the `myClass` prefix. By convention the name string is a camelCase version of the class name.</p>
<p>Event prefixes allow events with the same name fired from instances of different classes to be uniquely identified, when bubbled or broadcast. For instance, a `click` event on any Menu widget can be discerned by subscribing to the `menu:click` event; an Editor click would be distinguished as `editor:click`. Because YUI 3.x Custom Events bubble, this prefixing allows you to subscribe to events from specific classes at a higher level in your application &mdash; that is, you can listen from common event target, much the way you would when using event delegation when working with DOM events. For example:</p>
```
// NAME is used to prefix the provided event type, if not already prefixed,
// when publishing, firing and subscribing to events.
MyClass.prototype.doSomething = function() {
// Actually fires the event type "myclass:enabled"
this.fire("enabled");
}
...
var o = new MyClass(cfg);
o.on("enabled", function() {
// Actually listening for "myclass:enabled".
});
o.on("myclass:enabled", function() {
// Also listening for "myclass:enabled"
});
```
<p>The `NAME` property is also used in the default `toString` implementation for Base.</p>
<h3 id="attrsprop">ATTRS</h3>
<p>The `ATTRS` property is an associative array (an object with attribute name/configuration pairs) which is used to define the default set of
attributes that your class adds to each instance. The instance will contain attributes defined by each class in the class
hierarchy from which it is created, with each class adding the set of attributes and supporting code that it
requires.</p>
<p>For example, this is the (partial) set of attributes which the <a href="{{apiDocs}}/Drag.html">Drag</a> class defines:</p>
```
Drag.ATTRS = {
node: {
setter: function(node) {
var n = Y.one(node);
if (!n) {
Y.fail('DD.Drag: Invalid Node Given: ' + node);
}
return n;
}
},
dragNode: {
setter: function(node) {
var n = Y.one(node);
if (!n) {
Y.fail('DD.Drag: Invalid dragNode Given: ' + node);
}
return n;
}
},
offsetNode: {
value: true
},
clickPixelThresh: {
value: DDM.get('clickPixelThresh')
},
...
}; // End of Drag.ATTRS associative array (object literal)
```
<p>Each property in the object literal (e.g. `"dragNode"`), defines the name of the attribute to be added, and
the corresponding value defines the attribute's configuration. See <a href="../attribute/index.html#configuration">Attribute's discussion of configuration properties</a>
for more details about how configuration objects should be structured.</p>
<p>When instantiating a class derived from Base, Base's `init()` method will initialize
the set of attributes defined by the `ATTRS` property for each class in the class hierarchy. This helps avoid
replication of attribute initialization code in the constructor/initializer of each class.
</p>
<p>It also defines a specific order in which
attributes are initialized &mdash; starting from the Base class first and ending with the specific subclass being instantiated. Within a class, the order in which attributes are defined
in the `ATTRS` property does not matter. If an attribute defined in the `ATTRS` configuration for the class, requests the value of
another attribute defined after it in the `ATTRS` configuration (in its `valueFn` or `getter` for example),
the later attribute will be initialized on demand, when the first attribute attempts to `get` the value of the later attribute.
</p>
<p>
It is worth noting that Base adds or initializes attributes lazily for performance reasons, meaning the attribute will not be initialized until the first call to get or set it is made. This behavior
can be overridden if desired for specific attributes by setting the `lazyAdd` configuration property to `false` (for example if the `setter` for the attribute is responsible
for setting some other non attribute state in the object).
</p>
<h2 id="initdestroy">Initialization and Destruction</h2>
<p>Base implements final versions of its `init` and `destroy` methods used to establish the initialization and destruction lifecycle phases.
Classes extending Base can perform operations during initialization or destruction by defining prototype-level `initializer` and `destructor` methods:</p>
```
Y.extend(MyClass, Y.Base, {
// Prototype methods for your new class
// Tasks MyClass needs to perform during
// the init() lifecycle phase
initializer : function(cfg) {
this._wrapper = Y.Node.create('<div class="yui-wrapper"></div>');
},
// Tasks MyClass needs to perform during
// the destroy() lifecycle phase
destructor : function() {
Y.Event.purgeElement(this._wrapper);
this._wrapper.get("parentNode").removeChild(this._wrapper);
this._wrapper = null;
}
});
```
<p>Base's `init` and `destroy` methods take care of invoking `initializer` and `destructor` methods for each class in the hierarchy.
The implementations for each class do not need to call superclass versions of the method. Base ensures that initialization and destruction occur in a fixed order, following the class hierarchy.</p>
<dl>
<dt>`initializer()`</dt>
<dd>
Base's `init` method, which is invoked by Base's constructor, will invoke the `initializer` method for each class in the hierarchy &mdash; starting from the Base class first and ending with the subclass being instantiated.
The `initializer` method for each class is invoked after its attributes have been initialized (as discussed above) and will receive the configuration object literal passed to the `init` method.
</dd>
<dt>`destructor()`</dt>
<dd>
Base's `destroy` method, when called, will invoke the `destructor` method for each class in the hierarchy &mdash; starting from the subclass instantiated to create the instance and ending with the Base class (the opposite of initialization).
</dd>
</dl>
<p>If your class does not require any code to be executed during `init` or `destroy`, you do not need to define the corresponding `initializer` or `destructor` method on its prototype.</p>
<p>The <a href="{{componentAssets}}/mycomponent.js.txt">"MyComponent" template file</a> provides a starting point for you to create your own components derived from `Base`.</p>
<h2 id="plugins">Plugins</h2>
<p>
Plugins can be used to add atomic pieces of functionality or features to instances of objects derived from Base, without having to bake support, or even
knowledge of the feature, into the core object class. This allows features to be mixed and matched per instance, without having to build all features into a monolithic class or having to ship multiple
classes with varying permutations of features.
</p>
<p>
`Plugin.Host` adds the following key methods to the `Base` class:
</p>
<dl>
<dt>`<a href="{{apiDocs}}/Plugin.Host.html#method_plug">plug(pluginClass, pluginConfig)</a>`</dt>
<dd>
<p>Adds a plugin to the instance with the configuration specified. The `plug` method adds a new instance of the plugin
and attaches it to the instance on the namespace (property) defined by the plugin class' `NS` property.</p>
<p>The `plug` method also allows multiple plugins to be added in a single call by passing in an array of plugins with optional configurations as defined in the API documentation.</p>
</dd>
<dt>`<a href="{{apiDocs}}/Plugin.Host.html#method_unplug">unplug(pluginClass)</a>` or `<a href="{{apiDocs}}/Plugin.Host.html#method_unplug">unplug(namespace)</a>`</dt>
<dd>Removes the provided plugin or the plugin at the attached namespace from the instance and destroys it.</dd>
</dl>
<p>The above 2 methods are designed to be used after an instance of the component has already been created.
Plugins can also be added using the constructor configuration object, using the `plugins`
configuration key. For example:</p>
```
var overlay = new Y.Overlay({
srcNode: "#module",
plugins : [{fn:AnimPlugin, cfg:{duration:2}}]
});
```
<p>
Additionally, if the component developer wants a certain set of plugins added to his or her component by default, static <a href="{{apiDocs}}/Base.html#method_Base.plug">`Base.plug`</a> and <a href="{{apiDocs}}/Base.html#method_Base.unplug">`Base.unplug`</a>
methods are provided, allowing the developer to define the list of plugins to be added as part of the class definition.
</p>
<p>
The <a href="../plugin/index.html">Plugin landing page</a> discusses plugin development in detail. The <a href="../widget/widget-plugin.html">Widget IO Plugin</a>, <a href="../overlay/overlay-io-plugin.html">Overlay IO Plugin</a> and
<a href="../overlay/overlay-anim-plugin.html">Overlay Animation Plugin</a> examples also provide a concrete look at plugin development.
</p>
<p>
The <a href="{{componentAssets}}/../plugin/myplugin.js.txt">"MyPlugin" template file</a> provides a starting point for you to create your own plugins derived from `Plugin.Base`.
</p>
<h2 id="extensions">Extensions</h2>
<p>The `Base` class provides a static `build` method used to create custom classes, by mixing a main class, with one or more extension classes.</p>
<p>Extension classes are similar to plugins, in that they encapsulate or bundle specific feature implementations. However extensions are used to mix and match code at the class level, to create new classes, whereas plugins are used to mix and match code at the instance level.</p>
<h3>Build</h3>
<p>The `build` method can be used to dynamically create new classes that are derived from an existing main class and mix in additional "extension" classes to add methods, attributes, events and properties to the main class.
By default, `build` leaves the original main and mixed-in classes untouched so that the main class can still be used without the additional features mixed in. However `build` can be invoked with a configuration
option so that it modifies the main class if required.</p>
```
/* Main Class */
function Panel(cfg) {
Panel.superclass.constructor.apply(this, arguments);
}
Panel.ATTRS = {
// Panel attributes
close : { ... },
minimize : { ... },
shadow : { ... },
...
};
Y.extend(Panel, Y.Base, {
// Panel methods
show : function() { ... },
hide : function() { ... },
minimize : function() { ... }
};
/* Additional Resizable Feature */
function Resizable() {
this._initResizable();
}
Resizable.ATTRS = {
handles : { ... },
constrain : { ... }
};
Resizable.prototype = {
_initResizable : function() { ... }
lock : function() { ... }
};
/* Additional Modality Feature */
function Modal() {
this._initModality();
}
Modal.ATTRS = {
modal : { ... },
region : { ... }
};
Modal.prototype = {
_initModality : function() { ... },
showMask() : function() { ... },
hideMask() : function() { ... }
};
// Create a new class WindowPanel, which extends Panel, and
// combines methods/attributes from Resizable and Modal
var WindowPanel = Y.Base.build("windowPanel", Panel, [Resizable, Modal]);
var wp = new WindowPanel({
shadow: true,
modal: true,
handles:["e", "s", "se"]
});
wp.show();
wp.lock();
```
<p>Under the hood, `build`:</p>
<ul>
<li>Creates a new 'built' class by extending the main class passed in as the first argument.</li>
<li>Augments the list of feature classes (the 2nd argument) to the built class, so that it now has their prototype methods.</li>
<li>
Aggregates any known static properties on the built class. Properties to aggregate are defined by a _buildCfg.aggregates on the main class.
For example Base defines the `ATTRS` property as a property which needs to be aggregated, and Widget adds the `HTML_PARSER` property.
</li>
</ul>
<p>The new class constructor created by `build` will invoke the constructors for the main and feature classes when the new class is instantiated, during the init part of the lifecycle.</p>
<p>In addition to `build`, Base also provides the static `create` and `mix` methods, which are sugar methods on top of Base.build.</p>
<dl>
<dt><a href="{{apiDocs}}/Base.html#method_Base.create">`Base.create`</a></dt>
<dd>The `create` sugar method makes the task of creating a completely new class, which mixes in extensions, a lot more succint, by providing a way for the caller to pass in additional prototype and static properties which
will exist on the newly created class.</dd>
<dt><a href="{{apiDocs}}/Base.html#method_Base.mix">`Base.mix`</a></dt>
<dd>The `mix` sugar method on the other hand, can be used to add extensions into an already existing class.</dd>
</dl>
<p>See Base's <a href="{{apiDocs}}/Base.html">`API documentation`</a> for more details on the `build`, `create` and `mix` methods.</p>
<p>The <a href="{{componentAssets}}/myextension.js.txt">"MyExtension" template file</a> provides a starting point for you to create your own extensions.</p>