This page is obsolete (it uses jQuery UI 1.5). Please see the updated page.
OK, this is the final update to the widget subclassing. Rather than creating a new method, $.widget.subclass
, I created a single base widget
$.ui.widget
that does nothing but includes the Aspect-Oriented-Programming code and a subclassing method. I put everything in the $.ui
namespace (since namespacing plugins doesn't work anyway, all plugin names need to be globally unique). I removed the
callSuper
method, since this.callSuper('ui.widget', 'method', args)
is no better than just doing it straight,
$.ui.widget.prototype.method.apply(this, args)
.
Without further ado, here's the code (download):
// create the master widget
$.widget("ui.widget",{
// Aspect Oriented Programming tools from Justin Palmer's article
yield: null,
returnValues: { },
before: function(method, f) {
var original = this[method];
this[method] = function() {
f.apply(this, arguments);
return original.apply(this, arguments);
};
},
after: function(method, f) {
var original = this[method];
this[method] = function() {
this.returnValues[method] = original.apply(this, arguments);
return f.apply(this, arguments);
}
},
around: function(method, f) {
var original = this[method];
this[method] = function() {
this.yield = original;
return f.apply(this, arguments);
}
}
});
function object(o){
function F(){};
F.prototype = o;
return new F;
}
// create a widget subclass (always in the $.ui namespace)
$.ui.widget.subclass = function subclass(name) {
$.widget('ui.'+name); // Slightly inefficient to create a widget only to discard its prototype, but it's not too bad
var widget = $.ui[name]; // $.widget should return the object itself!
widget.subclass = subclass;
var superclass = this;
widget.prototype = object(this.prototype);
arguments[0] = widget.prototype; // add the the new methods to the prototype
$.extend.apply(null, arguments);
widget.defaults = object(this.defaults);
widget.getter = this.getter;
// Subtle point: we want to call superclass init and destroy if they exist
// (otherwise the user of this function would have to keep track of all that)
if (widget.prototype.hasOwnProperty('init')){;
var init = widget.prototype.init;
widget.prototype.init = function(){
superclass.prototype.init.apply(this);
init.apply(this);
}
};
if (widget.prototype.hasOwnProperty('destroy')){
var destroy = widget.prototype.destroy;
widget.prototype.destroy = function(){
destroy.apply(this);
superclass.prototype.destroy.apply(this);
}
}
return widget; // address my complaint above
};
And here are the samples from the Extending Widgets tutorial:
// Experiment 1
var Superbox = {
init: function(){
var self = this;
this.element.click(function(){
self.move();
});
},
move: function(){
this.element.css (this.newPoint());
},
newPoint: function(){
return {top: this.distance(), left: this.distance()};
},
distance: function(){
return Math.round (Math.random()*this.getData('distance'));
}
};
$.ui.widget.subclass ('superbox', Superbox);
$.ui.superbox.defaults = { distance: 200 };
$('#experiment1').superbox();
// Experiment 2
$.ui.superbox.subclass ('supererbox', {
// overriding and new methods
move: function(){
this.element.animate(this.newPoint(), this.getData('speed'));
},
home: function(){
this.element.animate({top:0, left:0}, this.getData('speed'));
}
});
$.ui.supererbox.defaults.speed = 'normal';
$('#experiment2').supererbox();
// Experiment 3
$.ui.supererbox.subclass('superboxwithtext', {
move: function(){
var count = this.getData('count') || 0;
++count;
this.setData('count', count);
this.element.text('Move number '+count);
$.ui.supererbox.prototype.move.call(this); // note that we could just as well use $.ui.superbox.move for the original, "jumpy" move
}
});
$('#experiment3').superboxwithtext();
// Experiment 4
$.fn.pulse = function (opts){
opts = $.extend ({}, $.fn.pulse.defaults, opts);
for (i = 0; i < opts.times; ++i){
this.animate({opacity: 0.1}, opts.speed).animate({opacity: 1}, opts.speed);
}
return this;
};
$.fn.pulse.defaults = {
speed: 'fast',
times: 2
};
$('#experiment4').supererbox().supererbox('before','move', function() {
this.element.pulse();
});
// Experiment 5
$('#experiment5').supererbox().supererbox('around','move', function() {
this.element.pulse();
this.yield();
this.element.pulse();
});
sydney phelps says:
impressive…we are very much amused ;)
October 26, 2008, 2:26 pmFelix Nagel says:
Mhh examples seem not to work. Am i stupid? Or is it the code ;-)
July 1, 2009, 3:54 pmDanny says:
@Felix:
July 5, 2009, 9:05 pmAs the note at the top of the post says, this article is obsolete (it relies on jQuery UI 1.5). Please see the updated and maintained version at http://bililite.nfshost.com/blog/extending-jquery-ui-widgets/