YUI 3: Plugin [beta]
Plugins allow you to unobtrusively add functionality to objects (referred to as the "host" object) which derive from Base, such as nodes and widgets.
Plugins can inherit from the Plugin.Base class, but this is not a hard requirement as we'll see later.
Plugins are used to add atomic pieces of functionality or features to component instances (hosts), without having to bake support or even knowledge of the feature into the component class. This allows features to be mixed and matched per component instance, without having to build all features into a monolithic component class or having to ship multiple versions of the component class with varying combinations of features.
More Information
- Examples: Plugin Utility in action.
- API Documentation: View the full API documentation for the Plugin Utility.
- Download: Plugin Utility as part of the full YUI Library.
- Free Hosting on our fast edge servers with combo-loading.
- License: BSD.
Getting Started
Include Dependencies
The easiest way to include the source files for Plugin and its dependencies is to add the YUI seed file to your page, using the following script tag, and allow the YUI instance to download any additional files which may be required:
<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>
<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>The YUI instance will automatically pull down Plugin's source files and any missing dependencies when the plugin module is used. This helps you avoid having to manually manage the list of files needed on your page to support multiple components while also optimizing your initial page weight by loading files only when they are required.
If you do want to include file dependencies manually on your page, the YUI Dependency Configurator can be used to determine the list of files you need to include in order to use Plugin.
The YUI Instance
Once you have the YUI seed file on your page (yui-min.js), you can
create a new YUI instance for your application to use and populate it
with the modules you need, specified as the first set of arguments to the
use method:
// Create new YUI instance, and populate it with the required modules YUI().use('plugin', function(Y) { // Plugin available, and ready for use. });
// Create new YUI instance, and populate it with the required modules YUI().use('plugin', function(Y) { // Plugin available, and ready for use. });
The last argument passed to use is a callback function. The callback function will be invoked as soon as the YUI instance is done downloading any required files missing from your page. Once those files are loaded, your local YUI instance will be supplemented with the classes which make up the plugin module and any modules it depends on. A reference to the populated YUI instance (Y) is passed back to your callback function. Within your callback, then, you can start writing your application code based on your own custom instance of YUI.
For more information on creating instances of YUI and the
use method, see the
YUI Global object documentation.
Creating Plugins
This section describes how to create and use plugins. It contains these subsections:
Simple Plugins
For the most basic plugins, which don't have any events or attributes of their own, and which don't modify the behavior of the host by listening for any host events, or overriding any of the host's methods, plugins can simply be basic JavaScript classes.
The only requirement for the class is that it has a static namespace property NS with a value assigned to it.
The value of the NS property is used to define the property on the host instance which will refer to
the plugin when it's plugged into the host.
When plugins are plugged into a host instance a new instance of the plugin is created, and a reference to the host is added to the configuration object passed to the plugins constructor, so that the plugin has a way to reference the host object. (similarly, when plugins are unplugged from a host object they are destroyed).
So, putting this all together, a simple plugin class may look something like the following:
// This AnchorPlugin is designed to be added to Node instances (the host will be a Node instance) function AnchorPlugin(config) { // Hold onto the host instance (a Node in this case), // for other plugin methods to use. this._node = config.host; } // When plugged into a node instance, the plugin will be // available on the "anchors" property. AnchorPlugin.NS = "anchors" AnchorPlugin.prototype = { disable: function() { var node = this._node; var anchors = node.queryAll("a"); anchors.addClass("disabled"); anchors.setAttribute("disabled", true); } };
// This AnchorPlugin is designed to be added to Node instances (the host will be a Node instance) function AnchorPlugin(config) { // Hold onto the host instance (a Node in this case), // for other plugin methods to use. this._node = config.host; } // When plugged into a node instance, the plugin will be // available on the "anchors" property. AnchorPlugin.NS = "anchors" AnchorPlugin.prototype = { disable: function() { var node = this._node; var anchors = node.queryAll("a"); anchors.addClass("disabled"); anchors.setAttribute("disabled", true); } };
To use the AnchorPlugin, the user would plug it into a Node reference they were holding on to:
var container = Y.one("div.actions"); container.plug(AnchorPlugin);
var container = Y.one("div.actions"); container.plug(AnchorPlugin);
And invoke methods on the plugin, through the namespace it is bound to:
container.anchors.disable();
container.anchors.disable();
Advanced Plugins
For basic features, simple plugin classes as described above may suffice. However, when you have more complex features which you'd like to encapsulate, the ability to use attributes and events for your plugin implementation becomes useful. More importantly, for many plugins, you'll be looking to modify the default behavior of the host instance in some way (for example an Animation Plugin may want to change the default show/hide behavior of a Widget, to be animated).
For these richer plugins, you should extend the base plugin class Plugin.Base.
Plugin is a subclass of Base, thereby providing managed attributes, lifecycle methods, and custom event support. Additionally it allows the plugin code to
either listen for and react to events fired by the host or inject custom logic before or after methods invoked on the host object (through the YUI 3 AOP infrastructure).
Extending Plugin.Base
You can extend the Plugin.Base class, just as you would extend the Base class. One thing to note when comparing this to simple plugins
is that the host reference is automatically set up as an attribute by the Plugin.Base class, so a reference to it does not need to be set up explicitly.
The class structure for an advanced plugin follows the pattern for all classes derived from Base, with the addition of the NS property to define
the namespace for the plugin (see the Base documentation for details about NAME and ATTRS).
// A plugin class designed to animate Widget's show and hide methods. function WidgetAnimPlugin(config) { WidgetAnimPlugin.superclass.constructor.apply(this, arguments); } // Define Static properties NAME (to identify the class) and NS (to identify the namespace) WidgetAnimPlugin.NAME = 'widgetAnimPlugin'; WidgetAnimPlugin.NS = 'fx'; // Attribute definitions for the plugin WidgetAnimPlugin.ATTRS = { animHidden : { ... }, animVisible: { ... } }; // Extend Plugin.Base Y.extend(WidgetAnimPlugin, Y.Plugin.Base, { // Add any required prototype methods });
// A plugin class designed to animate Widget's show and hide methods. function WidgetAnimPlugin(config) { WidgetAnimPlugin.superclass.constructor.apply(this, arguments); } // Define Static properties NAME (to identify the class) and NS (to identify the namespace) WidgetAnimPlugin.NAME = 'widgetAnimPlugin'; WidgetAnimPlugin.NS = 'fx'; // Attribute definitions for the plugin WidgetAnimPlugin.ATTRS = { animHidden : { ... }, animVisible: { ... } }; // Extend Plugin.Base Y.extend(WidgetAnimPlugin, Y.Plugin.Base, { // Add any required prototype methods });
Plugin Listeners
The main value obtained by extending Plugin.Base is the ability to react to events fired by the host or methods which are invoked on the host
using the doBefore and doAfter methods; and to do so
in such a way that any listeners added by the plugin are removed when the plugin is unplugged. This is important. Plugins should clean up after themselves when unplugged from the host.
Plugin.Base takes care of this for all listeners added through the doBefore and doAfter methods.
Events
As mentioned, plugins which derive from Plugin.Base have the ability to listen for events on the host object and react to them.
For example, all widgets fire a render event when they are rendered. Your widget specific plugin may need to know when this occurs,
so that it can inject custom elements into the markup the host renders for example. It can do this through the doAfter method:
// A plugin which introduces rounded corners to a widget. function RoundedCornersPlugin(config) { //... } RoundedCornersPlugin.NAME = 'roundedCornersPlugin'; RoundedCornersPlugin.NS = 'corners'; Y.extend(RoundedCornersPlugin, Y.Plugin.Base, { // Automatically called by Base, during construction initializer: function(config) { // "render" is a widget event this.doAfter('render', this.insertCornerElements); }, insertCornerElements: function() { var widget = this.get("host"); var boundingBox = widget.get("boundingBox"); var tl = Y.Node.create(TL_TEMPLATE); //... boundingBox.appendChild(tlNode); boundingBox.appendChild(trNode); boundingBox.appendChild(blNode); boundingBox.appendChild(brNode); } });
// A plugin which introduces rounded corners to a widget. function RoundedCornersPlugin(config) { //... } RoundedCornersPlugin.NAME = 'roundedCornersPlugin'; RoundedCornersPlugin.NS = 'corners'; Y.extend(RoundedCornersPlugin, Y.Plugin.Base, { // Automatically called by Base, during construction initializer: function(config) { // "render" is a widget event this.doAfter('render', this.insertCornerElements); }, insertCornerElements: function() { var widget = this.get("host"); var boundingBox = widget.get("boundingBox"); var tl = Y.Node.create(TL_TEMPLATE); //... boundingBox.appendChild(tlNode); boundingBox.appendChild(trNode); boundingBox.appendChild(blNode); boundingBox.appendChild(brNode); } });
Methods
In some cases, your plugin may need to override the logic in the host class' methods. The doBefore and doAfter methods provided by Plugin.Base can be used for methods
as well as events, allowing you to insert custom plugin logic before or after a method is executed on the host object.
For example, to animate the way a widget is shown or hidden, we may need to override the method which actually flips the visibility style attribute on the widget's bounding box and replace it with an animated opacity implementation, as shown below:
// A plugin class designed to animate Widget's show and hide methods. function WidgetAnimPlugin(config) { //... } WidgetAnimPlugin.NAME = 'widgetAnimPlugin'; WidgetAnimPlugin.NS = 'fx'; WidgetAnimPlugin.ATTRS = { animHidden : { //... }, animVisible: { //... } }; // Extend Plugin.Base, and override the default // method _uiSetVisible, used by Widget to flip the visibility Y.extend(WidgetAnimPlugin, Y.Plugin.Base, { initializer : function(config) { // Override Widget's _uiSetVisible method, with the custom animated method this.doBefore("_uiSetVisible", this._uiAnimSetVisible); }, _uiAnimSetVisible : function(show) { // Instead of flipping visibility, use the animation // instances configured for the plugin, to animate // hide/show. if (this.get("host").get("rendered")) { if (show) { this.get("animHidden").stop(); this.get("animVisible").run(); } else { this.get("animVisible").stop(); this.get("animHidden").run(); } // Prevent the default method from being invoked. return new Y.Do.Prevent(); } } });
// A plugin class designed to animate Widget's show and hide methods. function WidgetAnimPlugin(config) { //... } WidgetAnimPlugin.NAME = 'widgetAnimPlugin'; WidgetAnimPlugin.NS = 'fx'; WidgetAnimPlugin.ATTRS = { animHidden : { //... }, animVisible: { //... } }; // Extend Plugin.Base, and override the default // method _uiSetVisible, used by Widget to flip the visibility Y.extend(WidgetAnimPlugin, Y.Plugin.Base, { initializer : function(config) { // Override Widget's _uiSetVisible method, with the custom animated method this.doBefore("_uiSetVisible", this._uiAnimSetVisible); }, _uiAnimSetVisible : function(show) { // Instead of flipping visibility, use the animation // instances configured for the plugin, to animate // hide/show. if (this.get("host").get("rendered")) { if (show) { this.get("animHidden").stop(); this.get("animVisible").run(); } else { this.get("animVisible").stop(); this.get("animHidden").run(); } // Prevent the default method from being invoked. return new Y.Do.Prevent(); } } });
Support & Community
Forums & Blog
YUI 3 discussion forums are hosted on YUILibrary.com.
In addition, please visit the YUIBlog for updates and articles about the YUI Library written by the library's developers.
Filing Bugs & Feature Requests
The YUI Library's public bug tracking and feature request repositories are located on the YUILibrary.com site. Before filing new feature requests or bug reports, please review our reporting guidelines.

