aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott González <scott.gonzalez@gmail.com>2012-12-03 14:18:24 -0500
committerScott González <scott.gonzalez@gmail.com>2012-12-03 14:18:24 -0500
commit8b15aaf4964490a2a84e8f9d32d86ac750e0d4a2 (patch)
tree196747f3a110e8e3f88315baec82006665150cf8
parent7312933c888bce73460d88ec090d628b952a199e (diff)
downloadjquery-ui-8b15aaf4964490a2a84e8f9d32d86ac750e0d4a2.tar.gz
jquery-ui-8b15aaf4964490a2a84e8f9d32d86ac750e0d4a2.zip
Widget: Don't modify the prototype passed to $.widget(). Fixes #8876 - Calling _super calls wrong inherited widget.
-rw-r--r--tests/unit/widget/widget_core.js49
-rw-r--r--ui/jquery.ui.widget.js55
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( $( "<div>" ).testWidget1().testWidget1( "method" ),
+ "mixed testWidget1", "testWidget1 mixin successful" );
+ equal( $( "<div>" ).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,