I have lately been looking to become more involed with open source projects and for starters I have been looking at jQuery UI and jQuery Mobile. Both are amazing projects and share many of the same characteristics in their code. This is because in both cases, the majority of the functionality is inherited from the jQuery UI widget factory. If I’m going to become involved in this project I should understand how this works, so the following is my breakdown of the code as I understand it.
The widget factory looks like the following:
$.widget = function( name, base, prototype ) {
var namespace = name.split( "." )[ 0 ],
fullName;
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
// create selector for plugin
$.expr[ ":" ][ fullName ] = function( elem ) {
return !!$.data( elem, name );
};
/* OTHER STUFF */
}
I’ll get to the stuff in a second, but lets see what we’ve already learned here. First I see that widget can take up to three arguments (name, base, prototype), and example might be something like
$.widget("cg.awesomewidget, "ui.button", { /*...*/})
here name or “cg.awesomewidget” becomes the namespace and fullname so in my example I have namespaced it to “cg” with a fullname of “cg-awesomewidget”. I also see that we check if prototype is provided, if it is not we assume that we are not inheriting from a named widget, set the base parameter to prototype and set the base to the main $.Widget base object. Okay that sounds more messy than it is. Lets try to rephrase. The base is an optional parameter telling the widget factory we want to inherit from a known widget. In my example above its “ui.button”. If that parameter is not provided it simply pulls from the base $.Widget. So we know that any widget will carry the prototype base of $.Widget for starters. Now, what about this prototype? This is the base object literal that the widget makes its prototype. Sweet right? The next fun fact is that our widget gets its very own shiny new custom selector $(“:cg-awesomewidget”).
Next the object is constructed via the jQuery.extend() method as follows:
$[ namespace ] = $[ namespace ] || {};
// create the constructor using $.extend() so we can carry over any
// static properties stored on the existing constructor (if there is one)
$[ namespace ][ name ] = $.extend( function( options, element ) {
// allow instantiation without "new" keyword
if ( !this._createWidget ) {
return new $[ namespace ][ name ]( options, element );
}
// allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
if ( arguments.length ) {
this._createWidget( options, element );
}
}, $[ namespace ][ name ], { version: prototype.version } );
here the $[ namespace ][ name ] object is merged together with the prototype.version into the existing constructor as described in the comments. Then the options are passed along to the base. Again this is done via jQuery extend.
var basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options );
This is followed up with a call to $.each() that checks all the functions of the base and applies those to our new widget.
$.each( prototype, function( prop, value ) {
if ( $.isFunction( value ) ) {
prototype[ prop ] = (function() {
var _super = function() {
return base.prototype[ prop ].apply( this, arguments );
};
var _superApply = function( args ) {
return base.prototype[ prop ].apply( this, args );
};
return function() {
var __super = this._super,
__superApply = this._superApply,
returnValue;
this._super = _super;
this._superApply = _superApply;
returnValue = value.apply( this, arguments );
this._super = __super;
this._superApply = __superApply;
return returnValue;
};
}());
}
});
After all of this its time to put it all together. The widget prototype is now set via extend where we extend our basePrototype (widget) merging in the new widget and prototype. The last thing needed is a call to $.widget.bridge() which creates an instance of the object.
$[ namespace ][ name ].prototype = $.widget.extend( basePrototype, {
namespace: namespace,
widgetName: name,
widgetEventPrefix: name,
widgetBaseClass: fullName
}, prototype );
$.widget.bridge( name, $[ namespace ][ name ] );
That concludes our walkthrough of the jQuery UI widget factory. Fairly amazing when you look at how simple it is to create a widget based on this. A simple example is a look at jquery.ui.tooltip.js