From 8b15aaf4964490a2a84e8f9d32d86ac750e0d4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Mon, 3 Dec 2012 14:18:24 -0500 Subject: [PATCH] Widget: Don't modify the prototype passed to $.widget(). Fixes #8876 - Calling _super calls wrong inherited widget. --- tests/unit/widget/widget_core.js | 49 +++++++++++++++++++++++----- ui/jquery.ui.widget.js | 55 +++++++++++++++++--------------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 961e2ff62..8102b1f4f 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -13,18 +13,23 @@ TestHelpers.testJshint( "widget" ); test( "widget creation", function() { expect( 5 ); - var myPrototype = { - _create: function() {}, - creationTest: function() {} - }; + var method, + myPrototype = { + _create: function() { + equal( method, "_create", "create function is copied over" ); + }, + creationTest: function() { + equal( method, "creationTest", "random function is copied over" ); + } + }; $.widget( "ui.testWidget", myPrototype ); ok( $.isFunction( $.ui.testWidget ), "constructor was created" ); equal( typeof $.ui.testWidget.prototype, "object", "prototype was created" ); - equal( $.ui.testWidget.prototype._create, myPrototype._create, - "create function is copied over" ); - equal( $.ui.testWidget.prototype.creationTest, myPrototype.creationTest, - "random function is copied over" ); + method = "_create"; + $.ui.testWidget.prototype._create(); + method = "creationTest"; + $.ui.testWidget.prototype.creationTest(); equal( $.ui.testWidget.prototype.option, $.Widget.prototype.option, "option method copied over from base widget" ); }); @@ -1324,6 +1329,34 @@ test( "redefine - widgetEventPrefix", function() { }); +test( "mixins", function() { + expect( 2 ); + + var mixin = { + method: function() { + return "mixed " + this._super(); + } + }; + + $.widget( "ui.testWidget1", { + method: function() { + return "testWidget1"; + } + }); + $.widget( "ui.testWidget2", { + method: function() { + return "testWidget2"; + } + }); + $.widget( "ui.testWidget1", $.ui.testWidget1, mixin ); + $.widget( "ui.testWidget2", $.ui.testWidget2, mixin ); + + equal( $( "
" ).testWidget1().testWidget1( "method" ), + "mixed testWidget1", "testWidget1 mixin successful" ); + equal( $( "
" ).testWidget2().testWidget2( "method" ), + "mixed testWidget2", "testWidget2 mixin successful" ); +}); + asyncTest( "_delay", function() { expect( 6 ); var order = 0, diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 06f25576a..239492992 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -25,6 +25,9 @@ $.cleanData = function( elems ) { $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; @@ -71,38 +74,40 @@ $.widget = function( name, base, prototype ) { // inheriting from basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { - if ( $.isFunction( value ) ) { - prototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _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; - }; - })(); + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _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; + }; + })(); }); constructor.prototype = $.widget.extend( basePrototype, { // TODO: remove support for widgetEventPrefix // always use the name + a colon as the prefix, e.g., draggable:start // don't prefix for widgets that aren't DOM-based widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name - }, prototype, { + }, proxiedPrototype, { constructor: constructor, namespace: namespace, widgetName: name,