Augmenting YUI3 Module Objects

In YUI 2 augmenting a library class is as simple as appending functions to the static object or modifying the prototype of a non-static object. Augmenting objects in YUI 3 is almost as easy, differing only in the augmentation having to be applied inside of a YUI().add() function. Although, augmenting objects is not difficult, it feels complicated until you become used to the YUI 3 way of coding. This article will cover how to augment the objects in the YUI 3 library.

To start, think about your application. Ask yourself, if you include a common set of YUI 3 modules on every page, or if each page uses a disparate set. If you include the same modules files on every page, then you can consolidate your augmentation into a single file (yui3-ext.js), otherwise, you may want to have a file for the augmentation of each module (yui3-node.ext.js, yui3-widget-ext.js, etc.). When you want to apply your augmented objects, the location of the code needs to be defined in YUI() function.

Example 1: Making Augmentation Code Available to YUI()

 // single augmentation file
YUI({
	combine: true,
	modules: {
		yui3-ext: {
			fullpath: /js/yui3-ext.js,
			requires: [node, widget, cookie],
			optional: [],
			supersedes: []
		}
	}
}).use(yui3-ext, function() {
	// your code
});

// or multiple augmentation files
YUI({
	combine: true,
	modules: {
		yui3-node-ext: {
			fullpath: /js/yui3-node-ext.js,
			requires: [node],
			optional: [],
			supersedes: []
		},
		
		yui3-widget-ext: {
			fullpath: /js/yui3-widget-ext.js,
			requires: [widget],
			optional: [],
			supersedes: []
		}
	}
}).use(yui3-node-ext, yui3-widget-ext, function() {
	// your code
});

The first half of Example 1 shows how to setup a single augmentation file, which in this case assumes the application uses node and widget on each page. The second half of Example 1 shows how to define a specific augmentation file for each YUI3 module. Notice in both cases, when call the YUI().use() function only the module augmentation file is called. It is safe to rely on YUI 3 to use the requires property to dynamically load the core modules, you do not have to explicitly call them.

For the rest of this article we will be focusing on how to include a single augmentation file, to be used throughout your project.

Example 2: YUI3-EXT.js

YUI().add(yui3-ext, function(Y) {
	Y.mix(Y.Node.prototype, {
		
		/**
		 * Fetches normalizes tagName.
		 * @method getTagName
		 * @return {String} The tagName.
		 * @public
		 */
		getTagName: function() {
			return this.get(tagName).toLowerCase();
		},
	
		/**
		 * Toggles the className for the provided element as a result of the boolean.
		 * @method toggleClass
		 * @param className {String} Required. The class name to apply.
		 * @param bool {Boolean} Optional. Specify class state, instead of toggle.
		 * @return {Boolean} The class was added.
		 * @public
		 */
		toggleClass: function(className, bool) {
			bool = LANG.isUndefined(bool) ? ! this.hasClass(className) : bool;
			this[bool ? addClass : removeClass](className);
			return bool;
		},
		
		…
	});
	
	Y.mix(Y.Widget.prototype, {
		
		/**
		 * Undisplays the widget.
		 * @method hide
		 * @public
		 */
		hide: function() {
			this.get(boundingBox).addClass(displayNode);
		},
		
		/**
		 * Displays the widget.
		 * @method show
		 * @public
		 */
		show: function() {
			this.get(boundingBox).removeClass(displayNode);
		},
		
		…
	});
	
	Y.mix(Y.Cookie, {
		myCookieFunction: function() {
			// do something,
		},
		
		…
	});
}, "@VERSION@", {requires: [node, widget, cookie]});

Example 2 shows what should go inside of your augmentation JavaScript file. We register the name yui3-ext by calling YUI().add(yui3-ext, …);. This is how YUI 3 knows to execute this function block if the yui3-ext package is included in a YUI().use() function. YUI 3 first looks to see if the module is defined in the module list, then it fetches the JavaScript file as necessary, and finally calls the appropriate YUI.add() function defined in the fetched file. Inside of the add function the prototype of Y.Node and Y.Widget is augmented to have additional functions, and additional functions are added to the static Y.Cookie object. This shows how to augment instantiatable objects versus static ones.

Now, in your code, any time the yui3-ext package is included all instances of Y.Node and Y.Widget, and the static Y.Cookie object will have additional functions.

Example 3: Using YUI3-EXT

YUI({
	combine: true,
	modules: {
		yui3-ext: {
			fullpath: /js/yui3-ext.js,
			requires: [node, widget, cookie],
			optional: [],
			supersedes: []
		}
	}
}).use(yui3-ext, slider, function(Y) {
	var node = Y.get(#myNodeId);
	node.toggleClass(myClass);
	
	var slider = new Y.Slider({});
	slider.hide();
	slider.show();
	
	Y.Cookie.myCookieFunction();
});

In Example 3 we see that each YUI 3 module is augmented, but that developers do not need to do anything special to access the augmentations. The augmentations are now part of the objects and even in the case of Y.Slider, which inherits from Y.Widget, we still find the augmented widget methods.