{"id":137,"date":"2008-08-22T16:28:34","date_gmt":"2008-08-22T22:28:34","guid":{"rendered":"http:\/\/bililite.nfshost.com\/blog\/?p=137"},"modified":"2009-01-28T20:07:46","modified_gmt":"2009-01-29T02:07:46","slug":"extending-jquery-ui-widgets-the-final-chapter","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2008\/08\/22\/extending-jquery-ui-widgets-the-final-chapter\/","title":{"rendered":"Extending jQuery UI Widgets, The Final Chapter"},"content":{"rendered":"<p><strong>This page is obsolete (it uses jQuery UI 1.5). Please see <a href=\"\/blog\/extending-jquery-ui-widgets\/\">the updated page<\/a>.<\/strong><\/p>\r\n<p>OK, this is the final update to the widget subclassing. Rather than creating a new method, <code>$.widget.subclass<\/code>, I created a single base widget\r\n<code>$.ui.widget<\/code> that does nothing but includes the Aspect-Oriented-Programming code and a subclassing method. I put everything in the <code>$.ui<\/code>\r\nnamespace (since namespacing plugins doesn't work anyway, all plugin names need to be globally unique). I removed the\r\n<code>callSuper<\/code> method, since <code>this.callSuper('ui.widget', 'method', args)<\/code> is no better than just doing it straight, \r\n<code>$.ui.widget.prototype.method.apply(this, args)<\/code>.<\/p>\r\n<p>Without further ado, here's the code (<a href=\"\/inc\/ui.subclass.js\">download<\/a>):<\/p>\r\n<!--more-->\r\n<pre><code class=\"language-javascript demo\">\r\n\/\/ create the master widget\r\n$.widget(\"ui.widget\",{\r\n\t\/\/ Aspect Oriented Programming tools from Justin Palmer's article\r\n  yield: null,\r\n  returnValues: { },\r\n  before: function(method, f) {\r\n    var original = this[method];\r\n    this[method] = function() {\r\n      f.apply(this, arguments);\r\n      return original.apply(this, arguments);\r\n    };\r\n  },\r\n  after: function(method, f) {\r\n    var original = this[method];\r\n    this[method] = function() {\r\n      this.returnValues[method] = original.apply(this, arguments);\r\n      return f.apply(this, arguments);\r\n    }\r\n  },\r\n  around: function(method, f) {\r\n    var original = this[method];\r\n    this[method] = function() {\r\n      this.yield = original;\r\n      return f.apply(this, arguments);\r\n    }\r\n  }\r\n});\r\n\r\nfunction object(o){\r\n\tfunction F(){};\r\n\tF.prototype = o;\r\n\treturn new F;\r\n}\r\n\r\n\/\/ create a widget subclass (always in the $.ui namespace)\r\n$.ui.widget.subclass = function subclass(name) {\r\n\t$.widget('ui.'+name); \/\/ Slightly inefficient to create a widget only to discard its prototype, but it's not too bad\r\n\tvar widget = $.ui[name]; \/\/ $.widget should return the object itself!\r\n\twidget.subclass = subclass;\r\n\tvar superclass = this;\r\n\t\r\n\twidget.prototype = object(this.prototype);\r\n\targuments[0] = widget.prototype; \/\/ add the the new methods to the prototype\r\n\t$.extend.apply(null, arguments); \r\n\twidget.defaults = object(this.defaults);\r\n\twidget.getter = this.getter;\r\n\t\r\n\t\/\/ Subtle point: we want to call superclass init and destroy if they exist\r\n\t\/\/ (otherwise the user of this function would have to keep track of all that)\r\n\tif (widget.prototype.hasOwnProperty('init')){;\r\n\t  var init = widget.prototype.init;\r\n\t\twidget.prototype.init = function(){\r\n\t\t\tsuperclass.prototype.init.apply(this);\r\n\t\t\tinit.apply(this);\r\n\t\t}\r\n\t};\r\n\tif (widget.prototype.hasOwnProperty('destroy')){\r\n\t\tvar destroy = widget.prototype.destroy;\r\n\t\twidget.prototype.destroy = function(){\r\n\t\t\tdestroy.apply(this);\r\n\t\t\tsuperclass.prototype.destroy.apply(this);\r\n\t\t}\r\n\t}\r\n\treturn widget; \/\/ address my complaint above\r\n};\r\n<\/code><\/pre>\r\n<script>\r\n$(function(){\r\n\t$('.target').css({\r\n\t\tposition: 'relative',\r\n\t\tbackground: 'green',\r\n\t\tpadding: '5px',\r\n                margin: '10px 0',\r\n                border: '2px solid #0f0',\r\n\t\theight: '100px',\r\n\t\twidth: '100px',\r\n\t\tcolor: 'white'\r\n\t});\r\n\t$('#home').click(function(){\r\n\t\t$('.target').animate({top: 0, left:0}, 'fast');\r\n\t});\r\n});\r\n<\/script>\r\n<p>And here are the samples from the <a href=\"\/blog\/2008\/08\/13\/extending-jquery-ui-widgets-revisited\/\">Extending Widgets tutorial<\/a>:<\/p>\r\n<pre><code class=\"language-javascript demo\">\r\n\/\/  Experiment 1\r\nvar Superbox = {\r\n\tinit: function(){\r\n\t\tvar self = this;\r\n\t\tthis.element.click(function(){\r\n\t\t\tself.move();\r\n\t\t});\r\n\t},\r\n\tmove: function(){\r\n\t\tthis.element.css (this.newPoint());\r\n\t},\r\n\tnewPoint: function(){\r\n\t\treturn {top: this.distance(), left: this.distance()};\r\n\t},\t\r\n\tdistance: function(){\r\n\t\treturn Math.round (Math.random()*this.getData('distance'));\r\n\t}\r\n};\r\n$.ui.widget.subclass ('superbox', Superbox);\r\n$.ui.superbox.defaults = { distance: 200 };\r\n\r\n$('#experiment1').superbox();\r\n\r\n\/\/ Experiment 2\r\n$.ui.superbox.subclass ('supererbox', {\r\n\t\/\/ overriding and new methods\r\n\tmove: function(){\r\n\t\tthis.element.animate(this.newPoint(), this.getData('speed'));\r\n\t},\r\n\thome: function(){\r\n\t\tthis.element.animate({top:0, left:0}, this.getData('speed'));\r\n\t}\r\n});\r\n$.ui.supererbox.defaults.speed = 'normal';\r\n\r\n$('#experiment2').supererbox();\r\n\r\n\/\/ Experiment 3\r\n$.ui.supererbox.subclass('superboxwithtext',  {\r\n\tmove: function(){\r\n\t\tvar count = this.getData('count') || 0;\r\n\t\t++count;\r\n\t\tthis.setData('count', count);\r\n\t\tthis.element.text('Move number '+count);\r\n\t\t$.ui.supererbox.prototype.move.call(this); \/\/ note that we could just as well use $.ui.superbox.move for the original, \"jumpy\" move\r\n\t}\r\n});\r\n\r\n$('#experiment3').superboxwithtext();\r\n\r\n\/\/ Experiment 4\r\n$.fn.pulse = function (opts){\r\n\topts = $.extend ({}, $.fn.pulse.defaults, opts);\r\n\tfor (i = 0; i &lt; opts.times; ++i){\r\n\t\tthis.animate({opacity: 0.1}, opts.speed).animate({opacity: 1}, opts.speed);\r\n\t}\r\n\treturn this;\r\n};\r\n$.fn.pulse.defaults = {\r\n\tspeed: 'fast',\r\n\ttimes: 2\r\n};\r\n$('#experiment4').supererbox().supererbox('before','move', function() {\r\n\tthis.element.pulse();\r\n});\r\n\r\n\/\/ Experiment 5\r\n$('#experiment5').supererbox().supererbox('around','move', function() {\r\n\tthis.element.pulse();\r\n\tthis.yield();\r\n\tthis.element.pulse();\r\n});\r\n\r\n<\/code><\/pre>\r\n<div><input type=\"button\" id=\"home\" value=\"Bring Them Home\"\/><\/div>\r\n<div class=\"target\" id=\"experiment1\">Experiment 1 (Click Me)<\/div>\r\n<div class=\"target\" id=\"experiment2\">Experiment 2 (Click Me)<\/div>\r\n<div class=\"target\" id=\"experiment3\">Experiment 3 (Click Me)<\/div>\r\n<div class=\"target\" id=\"experiment4\">Experiment 4 (Click Me)<\/div>\r\n<div class=\"target\" id=\"experiment5\">Experiment 5 (Click Me)<\/div>\r\n","protected":false},"excerpt":{"rendered":"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 [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/137"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=137"}],"version-history":[{"count":12,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/137\/revisions"}],"predecessor-version":[{"id":144,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/137\/revisions\/144"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=137"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=137"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}