Widget: Don't modify the prototype passed to $.widget(). Fixes #8876 - Calling _super calls wrong inherited widget.

This commit is contained in:
Scott González 2012-12-03 14:18:24 -05:00
parent 7312933c88
commit 8b15aaf496
2 changed files with 71 additions and 33 deletions

View File

@ -13,18 +13,23 @@ TestHelpers.testJshint( "widget" );
test( "widget creation", function() { test( "widget creation", function() {
expect( 5 ); expect( 5 );
var myPrototype = { var method,
_create: function() {}, myPrototype = {
creationTest: function() {} _create: function() {
}; equal( method, "_create", "create function is copied over" );
},
creationTest: function() {
equal( method, "creationTest", "random function is copied over" );
}
};
$.widget( "ui.testWidget", myPrototype ); $.widget( "ui.testWidget", myPrototype );
ok( $.isFunction( $.ui.testWidget ), "constructor was created" ); ok( $.isFunction( $.ui.testWidget ), "constructor was created" );
equal( typeof $.ui.testWidget.prototype, "object", "prototype was created" ); equal( typeof $.ui.testWidget.prototype, "object", "prototype was created" );
equal( $.ui.testWidget.prototype._create, myPrototype._create, method = "_create";
"create function is copied over" ); $.ui.testWidget.prototype._create();
equal( $.ui.testWidget.prototype.creationTest, myPrototype.creationTest, method = "creationTest";
"random function is copied over" ); $.ui.testWidget.prototype.creationTest();
equal( $.ui.testWidget.prototype.option, $.Widget.prototype.option, equal( $.ui.testWidget.prototype.option, $.Widget.prototype.option,
"option method copied over from base widget" ); "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() { asyncTest( "_delay", function() {
expect( 6 ); expect( 6 );
var order = 0, var order = 0,

View File

@ -25,6 +25,9 @@ $.cleanData = function( elems ) {
$.widget = function( name, base, prototype ) { $.widget = function( name, base, prototype ) {
var fullName, existingConstructor, constructor, basePrototype, 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 ]; namespace = name.split( "." )[ 0 ];
name = name.split( "." )[ 1 ]; name = name.split( "." )[ 1 ];
@ -71,38 +74,40 @@ $.widget = function( name, base, prototype ) {
// inheriting from // inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options ); basePrototype.options = $.widget.extend( {}, basePrototype.options );
$.each( prototype, function( prop, value ) { $.each( prototype, function( prop, value ) {
if ( $.isFunction( value ) ) { if ( !$.isFunction( value ) ) {
prototype[ prop ] = (function() { proxiedPrototype[ prop ] = value;
var _super = function() { return;
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;
};
})();
} }
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, { constructor.prototype = $.widget.extend( basePrototype, {
// TODO: remove support for widgetEventPrefix // TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start // always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based // don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
}, prototype, { }, proxiedPrototype, {
constructor: constructor, constructor: constructor,
namespace: namespace, namespace: namespace,
widgetName: name, widgetName: name,