diff --git a/Gruntfile.js b/Gruntfile.js index 3e66e2e37d3..d91dedb9bc4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -229,6 +229,9 @@ grunt.initConfig({ "qunit/qunit.css": "qunit/qunit/qunit.css", "qunit/LICENSE.txt": "qunit/LICENSE.txt", + "qunit-assert-classes/qunit-assert-classes.js": "qunit-assert-classes/qunit-assert-classes.js", + "qunit-assert-classes/LICENSE.txt": "qunit-assert-classes/LICENSE", + "jquery-mousewheel/jquery.mousewheel.js": "jquery-mousewheel/jquery.mousewheel.js", "jquery-mousewheel/LICENSE.txt": "jquery-mousewheel/LICENSE.txt", diff --git a/bower.json b/bower.json index 38a9f77ee54..9b6b71b6f36 100644 --- a/bower.json +++ b/bower.json @@ -15,6 +15,7 @@ "jquery-simulate": "1.0.0", "jshint": "2.4.4", "qunit": "1.17.1", + "qunit-assert-classes": "0.1.5", "jquery-1.7.0": "jquery#1.7.0", "jquery-1.7.1": "jquery#1.7.1", diff --git a/demos/autocomplete/combobox.html b/demos/autocomplete/combobox.html index 437e52ab1d2..f8898094dc8 100644 --- a/demos/autocomplete/combobox.html +++ b/demos/autocomplete/combobox.html @@ -58,7 +58,9 @@ source: $.proxy( this, "_source" ) }) .tooltip({ - tooltipClass: "ui-state-highlight" + classes: { + "ui-tooltip": "ui-state-highlight" + } }); this._on( this.input, { diff --git a/demos/droppable/accepted-elements.html b/demos/droppable/accepted-elements.html index 19db546508d..2a50295260d 100644 --- a/demos/droppable/accepted-elements.html +++ b/demos/droppable/accepted-elements.html @@ -20,8 +20,10 @@ $( "#draggable, #draggable-nonvalid" ).draggable(); $( "#droppable" ).droppable({ accept: "#draggable", - activeClass: "ui-state-hover", - hoverClass: "ui-state-active", + classes: { + "ui-droppable-active": "ui-state-active", + "ui-droppable-hover": "ui-state-hover" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) diff --git a/demos/droppable/photo-manager.html b/demos/droppable/photo-manager.html index 6323e5d7baa..6a8cf33d18b 100644 --- a/demos/droppable/photo-manager.html +++ b/demos/droppable/photo-manager.html @@ -47,7 +47,9 @@ // let the trash be droppable, accepting the gallery items $trash.droppable({ accept: "#gallery > li", - activeClass: "ui-state-highlight", + classes: { + "ui-droppable-active": "ui-state-highlight" + }, drop: function( event, ui ) { deleteImage( ui.draggable ); } @@ -56,7 +58,9 @@ // let the gallery be droppable as well, accepting items from the trash $gallery.droppable({ accept: "#trash li", - activeClass: "custom-state-active", + classes: { + "ui-droppable-active": "custom-state-active" + }, drop: function( event, ui ) { recycleImage( ui.draggable ); } diff --git a/demos/droppable/propagation.html b/demos/droppable/propagation.html index 60cf7cb377e..99fb2171187 100644 --- a/demos/droppable/propagation.html +++ b/demos/droppable/propagation.html @@ -21,8 +21,10 @@ $( "#draggable" ).draggable(); $( "#droppable, #droppable-inner" ).droppable({ - activeClass: "ui-state-hover", - hoverClass: "ui-state-active", + classes: { + "ui-droppable-active": "ui-state-active", + "ui-droppable-hover": "ui-state-hover" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) @@ -34,8 +36,10 @@ $( "#droppable2, #droppable2-inner" ).droppable({ greedy: true, - activeClass: "ui-state-hover", - hoverClass: "ui-state-active", + classes: { + "ui-droppable-active": "ui-state-active", + "ui-droppable-hover": "ui-state-hover" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) diff --git a/demos/droppable/revert.html b/demos/droppable/revert.html index 74ccb6fc545..f34801e90a6 100644 --- a/demos/droppable/revert.html +++ b/demos/droppable/revert.html @@ -21,8 +21,10 @@ $( "#draggable2" ).draggable({ revert: "invalid" }); $( "#droppable" ).droppable({ - activeClass: "ui-state-default", - hoverClass: "ui-state-hover", + classes: { + "ui-droppable-active": "ui-state-active", + "ui-droppable-hover": "ui-state-hover" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) diff --git a/demos/droppable/shopping-cart.html b/demos/droppable/shopping-cart.html deleted file mode 100644 index 0304004edc7..00000000000 --- a/demos/droppable/shopping-cart.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - jQuery UI Droppable - Shopping Cart Demo - - - - - - - - - - - - - - - -
-

Products

-
-

T-Shirts

-
-
    -
  • Lolcat Shirt
  • -
  • Cheezeburger Shirt
  • -
  • Buckit Shirt
  • -
-
-

Bags

-
-
    -
  • Zebra Striped
  • -
  • Black Leather
  • -
  • Alligator Leather
  • -
-
-

Gadgets

-
-
    -
  • iPhone
  • -
  • iPod
  • -
  • iPad
  • -
-
-
-
- -
-

Shopping Cart

-
-
    -
  1. Add your items here
  2. -
-
-
- -
-

Demonstrate how to use an accordion to structure products into a catalog and make use of drag and drop for adding them to a shopping cart, where they are sortable.

-
- - diff --git a/demos/droppable/visual-feedback.html b/demos/droppable/visual-feedback.html index 9b197c56e07..9852f371b05 100644 --- a/demos/droppable/visual-feedback.html +++ b/demos/droppable/visual-feedback.html @@ -20,7 +20,9 @@ $(function() { $( "#draggable" ).draggable(); $( "#droppable" ).droppable({ - hoverClass: "ui-state-hover", + classes: { + "ui-droppable-hover": "ui-state-hover" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) @@ -32,7 +34,9 @@ $( "#draggable2" ).draggable(); $( "#droppable2" ).droppable({ accept: "#draggable2", - activeClass: "ui-state-default", + classes: { + "ui-droppable-active": "ui-state-default" + }, drop: function( event, ui ) { $( this ) .addClass( "ui-state-highlight" ) @@ -66,7 +70,7 @@

Feedback on activating draggable:

-

Change the droppable's appearance on hover, or when the droppable is active (an acceptable draggable is dropped on it). Use the hoverClass or activeClass options to specify respective classes.

+

Change the droppable's appearance on hover, or when the droppable is active (an acceptable draggable is dropped on it). Set the values of the ui-droppable-hover or ui-droppable-active properties on the classes option to specify the respective classes.

diff --git a/external/qunit-assert-classes/LICENSE.txt b/external/qunit-assert-classes/LICENSE.txt new file mode 100644 index 00000000000..938db036815 --- /dev/null +++ b/external/qunit-assert-classes/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alexander Schmitz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/external/qunit-assert-classes/qunit-assert-classes.js b/external/qunit-assert-classes/qunit-assert-classes.js new file mode 100644 index 00000000000..f61046bc85b --- /dev/null +++ b/external/qunit-assert-classes/qunit-assert-classes.js @@ -0,0 +1,47 @@ +( function( QUnit ) { + function inArray( haystack, needle ) { + for ( var i = 0; i < haystack.length; i++ ) { + if ( haystack[ i ] === needle ) { + return true; + } + } + return false; + } + function check( element, classes, stateVal, message ) { + var i, result, classAttribute, elementClassArray, + classArray = classes.split( " " ), + missing = [], + found = []; + + if ( element.jquery && element.length !== 1 ) { + throw( "Class checks can only be performed on a single element on a collection" ); + } + element = element.jquery ? element[ 0 ] : element; + classAttribute = element.getAttribute( "class" ); + message = message || "Element must " + ( stateVal? "" : "not " ) + "have classes"; + if ( classAttribute ) { + elementClassArray = classAttribute.split( " " ); + for( i = 0; i < classArray.length; i++ ) { + if ( !inArray( elementClassArray, classArray[ i ] ) ) { + missing.push( classArray[ i ] ); + } else { + found.push( classArray[ i ] ); + } + } + } else { + missing = classArray; + } + + result = stateVal ? !missing.length : !found.length; + QUnit.push( result, classes, result ? classes : found.join( " " ), message ); + } + + QUnit.extend( QUnit.assert, { + hasClasses: function( element, classes, message ) { + check( element, classes, true, message ); + }, + lacksClasses: function( element, classes, message ) { + check( element, classes, false, message ); + } + }); +})( QUnit ); \ No newline at end of file diff --git a/tests/unit/accordion/accordion.html b/tests/unit/accordion/accordion.html index 0a8755fd34b..9ea2d364539 100644 --- a/tests/unit/accordion/accordion.html +++ b/tests/unit/accordion/accordion.html @@ -9,6 +9,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
...
+
+ Please share some personal information + + +
+
+

Some more (optional) information

+ +
+
+
+
+
+ + diff --git a/tests/unit/dialog/dialog_deprecated.js b/tests/unit/dialog/dialog_deprecated.js new file mode 100644 index 00000000000..55c18045739 --- /dev/null +++ b/tests/unit/dialog/dialog_deprecated.js @@ -0,0 +1,27 @@ +(function( $ ) { + +module( "dialog (deprecated): options" ); + +test( "dialogClass", function( assert ) { + expect( 5 ); + + var element = $( "
" ).dialog(), + widget = element.dialog( "widget" ); + assert.lacksClasses( widget, "foo", "dialogClass not specified. class not added" ); + element.remove(); + + element = $( "
" ).dialog({ dialogClass: "foo" }); + widget = element.dialog( "widget" ); + assert.hasClasses( widget, "foo", "dialogClass in init, foo class added" ); + element.dialog( "option", "dialogClass", "foobar" ); + assert.lacksClasses( widget, "foo", "dialogClass changed, previous one was removed" ); + assert.hasClasses( widget, "foobar", "dialogClass changed, new one was added" ); + element.remove(); + + element = $( "
" ).dialog({ dialogClass: "foo bar" }); + widget = element.dialog( "widget" ); + assert.hasClasses( widget, "foo bar", "dialogClass in init, two classes." ); + element.remove(); +}); + +})( jQuery ); diff --git a/tests/unit/dialog/dialog_methods.js b/tests/unit/dialog/dialog_methods.js index ea4a7002a22..c92f1f1b4a7 100644 --- a/tests/unit/dialog/dialog_methods.js +++ b/tests/unit/dialog/dialog_methods.js @@ -100,13 +100,12 @@ test("#4980: Destroy should place element back in original DOM position", functi ok($.contains(container[0], modal[0]), "dialog(destroy) should place element back in original DOM position"); }); -test( "enable/disable disabled", function() { - expect( 4 ); +test( "enable/disable disabled", function( assert ) { + expect( 3 ); var element = $( "
" ).dialog(); element.dialog( "disable" ); equal(element.dialog( "option", "disabled" ), false, "disable method doesn't do anything" ); - ok( !element.dialog( "widget" ).hasClass( "ui-dialog-disabled" ), "disable method doesn't add ui-dialog-disabled class" ); - ok( !element.dialog( "widget" ).hasClass( "ui-state-disabled" ), "disable method doesn't add ui-state-disabled class" ); + assert.lacksClasses( element, "ui-dialog-disabled ui-state-disabled", "disable method doesn't add classes" ); ok( !element.dialog( "widget" ).attr( "aria-disabled" ), "disable method doesn't add aria-disabled" ); }); diff --git a/tests/unit/dialog/dialog_options.js b/tests/unit/dialog/dialog_options.js index 3e5444c67e2..3b18df5601d 100644 --- a/tests/unit/dialog/dialog_options.js +++ b/tests/unit/dialog/dialog_options.js @@ -78,7 +78,7 @@ test("autoOpen", function() { element.remove(); }); -test("buttons", function() { +test("buttons", function( assert ) { expect(21); var btn, i, newButtons, @@ -105,8 +105,8 @@ test("buttons", function() { i++; }); - ok(btn.parent().hasClass("ui-dialog-buttonset"), "buttons in container"); - ok(element.parent().hasClass("ui-dialog-buttons"), "dialog wrapper adds class about having buttons"); + assert.hasClasses( btn.parent(), "ui-dialog-buttonset" ); + assert.hasClasses( element.parent(), "ui-dialog-buttons" ); btn.trigger("click"); @@ -136,12 +136,11 @@ test("buttons", function() { btn = element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" ); equal(btn.length, 0, "all buttons have been removed"); equal(element.find(".ui-dialog-buttonset").length, 0, "buttonset has been removed"); - equal(element.parent().hasClass("ui-dialog-buttons"), false, "dialog wrapper removes class about having buttons"); - + assert.lacksClasses( element.parent(), "ui-dialog-buttons" ); element.remove(); }); -test("buttons - advanced", function() { +test("buttons - advanced", function( assert ) { expect( 7 ); var buttons, @@ -166,7 +165,7 @@ test("buttons - advanced", function() { equal(buttons.length, 1, "correct number of buttons"); equal(buttons.attr("id"), "my-button-id", "correct id"); equal(buttons.text(), "a button", "correct label"); - ok(buttons.hasClass("additional-class"), "additional classes added"); + assert.hasClasses( buttons, "additional-class" ); deepEqual( buttons.button("option", "icons"), { primary: "ui-icon-cancel", secondary: null } ); equal( buttons.button( "option", "text" ), false ); buttons.click(); @@ -224,26 +223,6 @@ test("closeText", function() { element.remove(); }); -test("dialogClass", function() { - expect( 6 ); - - var element = $("
").dialog(); - equal(element.dialog("widget").is(".foo"), false, "dialogClass not specified. foo class added"); - element.remove(); - - element = $("
").dialog({ dialogClass: "foo" }); - equal(element.dialog("widget").is(".foo"), true, "dialogClass in init. foo class added"); - element.dialog( "option", "dialogClass", "foobar" ); - equal( element.dialog("widget").is(".foo"), false, "dialogClass changed, previous one was removed" ); - equal( element.dialog("widget").is(".foobar"), true, "dialogClass changed, new one was added" ); - element.remove(); - - element = $("
").dialog({ dialogClass: "foo bar" }); - equal(element.dialog("widget").is(".foo"), true, "dialogClass in init, two classes. foo class added"); - equal(element.dialog("widget").is(".bar"), true, "dialogClass in init, two classes. bar class added"); - element.remove(); -}); - test("draggable", function() { expect(4); diff --git a/tests/unit/draggable/draggable.html b/tests/unit/draggable/draggable.html index eb4985adc37..a8fdf91ca5b 100644 --- a/tests/unit/draggable/draggable.html +++ b/tests/unit/draggable/draggable.html @@ -68,6 +68,7 @@ + + + + + + + + + + + + + + + + + + + + + +
+
+ +
Draggable
+
Droppable
+
Droppable
+
 
+ +
+ + diff --git a/tests/unit/droppable/droppable_events.js b/tests/unit/droppable/droppable_events.js index 0e1424245be..1b205bc9736 100644 --- a/tests/unit/droppable/droppable_events.js +++ b/tests/unit/droppable/droppable_events.js @@ -2,7 +2,7 @@ module( "droppable: events" ); -test( "droppable destruction/recreation on drop event", function() { +test( "droppable destruction/recreation on drop event", function( assert ) { expect( 1 ); var config = { @@ -32,7 +32,7 @@ test( "droppable destruction/recreation on drop event", function() { dy: dy }); - ok( !droppable2.hasClass( "active" ), "subsequent droppable no longer active" ); + assert.lacksClasses( droppable2, "active", "subsequent droppable no longer active" ); }); // todo: comment the following in when ready to actually test diff --git a/tests/unit/droppable/droppable_methods.js b/tests/unit/droppable/droppable_methods.js index a8a2a078c2e..aebaf490c71 100644 --- a/tests/unit/droppable/droppable_methods.js +++ b/tests/unit/droppable/droppable_methods.js @@ -63,7 +63,7 @@ test("enable", function() { equal(actual, expected, "enable is chainable"); }); -test( "disable", function() { +test( "disable", function( assert ) { expect( 10 ); var actual, expected, @@ -77,9 +77,9 @@ test( "disable", function() { element.droppable({ disabled: false }); TestHelpers.droppable.shouldDrop(); element.droppable( "option", "disabled", true ); - ok( !element.droppable( "widget" ).hasClass( "ui-state-disabled" ), "element does not get ui-state-disabled" ); + assert.lacksClasses( element.droppable( "widget" ), "ui-state-disabled" ); ok( !element.droppable( "widget" ).attr( "aria-disabled" ), "element does not get aria-disabled" ); - ok( element.droppable( "widget" ).hasClass( "ui-droppable-disabled" ), "element gets ui-droppable-disabled" ); + assert.hasClasses( element.droppable( "widget" ), "ui-droppable-disabled" ); equal( element.droppable( "option", "disabled" ), true, "disabled option setter" ); TestHelpers.droppable.shouldNotDrop(); diff --git a/tests/unit/droppable/droppable_options.js b/tests/unit/droppable/droppable_options.js index 047858abf11..9a4be076d17 100644 --- a/tests/unit/droppable/droppable_options.js +++ b/tests/unit/droppable/droppable_options.js @@ -22,17 +22,18 @@ test( "activeClass", function() { ok(false, 'missing test - untested code is broken code'); }); */ -test( "{ addClasses: true }, default", function() { +test( "{ addClasses: true }, default", function( assert ) { expect( 1 ); var el = $( "
" ).droppable({ addClasses: true }); - ok( el.is( ".ui-droppable" ), "'ui-droppable' class added" ); + assert.hasClasses( el, "ui-droppable" ); el.droppable( "destroy" ); }); -test( "{ addClasses: false }", function() { +test( "{ addClasses: false }", function( assert ) { expect( 1 ); var el = $( "
" ).droppable({ addClasses: false }); - ok( !el.is( ".ui-droppable" ), "'ui-droppable' class not added" ); + + assert.lacksClasses( el, "ui-droppable" ); el.droppable( "destroy" ); }); diff --git a/tests/unit/effects/effects.html b/tests/unit/effects/effects.html index a092ce04bcf..352224a7b99 100644 --- a/tests/unit/effects/effects.html +++ b/tests/unit/effects/effects.html @@ -9,6 +9,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ anchor + + aria-describedby + span + baz +
+ +
+ +
+ +
+ + diff --git a/tests/unit/tooltip/tooltip_deprecated.js b/tests/unit/tooltip/tooltip_deprecated.js new file mode 100644 index 00000000000..a53a2705091 --- /dev/null +++ b/tests/unit/tooltip/tooltip_deprecated.js @@ -0,0 +1,13 @@ +(function( $ ) { + +module( "tooltip: (deprecated) options" ); + +test( "tooltipClass", function( assert ) { + expect( 1 ); + var element = $( "#tooltipped1" ).tooltip({ + tooltipClass: "custom" + }).tooltip( "open" ); + assert.hasClasses( $( "#" + element.data( "ui-tooltip-id" ) ), "custom" ); +}); + +}( jQuery ) ); diff --git a/tests/unit/tooltip/tooltip_methods.js b/tests/unit/tooltip/tooltip_methods.js index e6641a14ac8..ea69216d097 100644 --- a/tests/unit/tooltip/tooltip_methods.js +++ b/tests/unit/tooltip/tooltip_methods.js @@ -53,7 +53,7 @@ test( "open/close with tracking", function() { $.fx.off = false; }); -test( "enable/disable", function() { +test( "enable/disable", function( assert ) { expect( 11 ); $.fx.off = true; var tooltip, @@ -67,9 +67,9 @@ test( "enable/disable", function() { element.tooltip( "disable" ); equal( $( ".ui-tooltip" ).length, 0, "no tooltip when disabled" ); - ok( !element.tooltip( "widget" ).hasClass( "ui-state-disabled" ), "element doesn't get ui-state-disabled" ); + assert.lacksClasses( element.tooltip( "widget" ), "ui-state-disabled" ); ok( !element.tooltip( "widget" ).attr( "aria-disabled" ), "element doesn't get aria-disabled" ); - ok( !element.tooltip( "widget" ).hasClass( "ui-tooltip-disabled" ), "element doesn't get ui-tooltip-disabled" ); + assert.lacksClasses( element.tooltip( "widget" ), "ui-tooltip-disabled" ); strictEqual( tooltip.attr( "title" ), undefined, "title removed on disable" ); element.tooltip( "open" ); diff --git a/tests/unit/tooltip/tooltip_options.js b/tests/unit/tooltip/tooltip_options.js index 32f7395446a..1d9d6b3f09d 100644 --- a/tests/unit/tooltip/tooltip_options.js +++ b/tests/unit/tooltip/tooltip_options.js @@ -166,14 +166,6 @@ test( "items", function() { element.tooltip( "destroy" ); }); -test( "tooltipClass", function() { - expect( 1 ); - var element = $( "#tooltipped1" ).tooltip({ - tooltipClass: "custom" - }).tooltip( "open" ); - ok( $( "#" + element.data( "ui-tooltip-id" ) ).hasClass( "custom" ) ); -}); - test( "track + show delay", function() { expect( 2 ); var event, diff --git a/tests/unit/widget/widget.html b/tests/unit/widget/widget.html index 2b764abab9e..ac106ea3294 100644 --- a/tests/unit/widget/widget.html +++ b/tests/unit/widget/widget.html @@ -9,6 +9,7 @@ + + diff --git a/tests/unit/widget/widget_classes.js b/tests/unit/widget/widget_classes.js new file mode 100644 index 00000000000..0202d26831c --- /dev/null +++ b/tests/unit/widget/widget_classes.js @@ -0,0 +1,143 @@ +(function( $ ) { + +module( "widget factory classes", { + setup: function() { + $.widget( "ui.classesWidget", { + options: { + classes: { + "ui-classes-widget": "ui-theme-widget", + "ui-classes-element": "ui-theme-element ui-theme-element-2" + } + }, + _create: function() { + this.span = $( "" ) + .appendTo( this.element ); + + this.element.wrap( "
" ); + + this.wrapper = this.element.parent(); + + this._addClass( "ui-classes-element", "ui-core-element" ) + ._addClass( "ui-classes-element-2" ) + ._addClass( null, "ui-core-element-null" ) + ._addClass( this.span, null, "ui-core-span-null" ) + ._addClass( this.span, "ui-classes-span", "ui-core-span" ) + ._addClass( this.wrapper, "ui-classes-widget" ); + + }, + toggleClasses: function( bool ) { + this._toggleClass( "ui-classes-element", "ui-core-element", bool ) + ._toggleClass( "ui-classes-element-2", null, bool ) + ._toggleClass( null, "ui-core-element-null", bool ) + ._toggleClass( this.span, null, "ui-core-span-null", bool ) + ._toggleClass( this.span, "ui-classes-span", "ui-core-span", bool ) + ._toggleClass( this.wrapper, "ui-classes-widget", null, bool ); + }, + removeClasses: function() { + this._removeClass( "ui-classes-element", "ui-core-element" ) + ._removeClass( "ui-classes-element-2" ) + ._removeClass( null, "ui-core-element-null" ) + ._removeClass( this.span, null, "ui-core-span-null" ) + ._removeClass( this.span, "ui-classes-span", "ui-core-span" ) + ._removeClass( this.wrapper, "ui-classes-widget" ); + }, + _destroy: function() { + this.span.remove(); + this.element.unwrap(); + } + }); + }, + teardown: function() { + delete $.ui.classesWidget; + delete $.fn.classesWidget; + } +}); + +function elementHasClasses( widget, method, assert ) { + var toggle = method === "toggle" ? ( ", true" ) : ""; + + assert.hasClasses( widget, "ui-classes-element ui-theme-element ui-theme-element-2", + "_" + method + "Class works with ( keys, extra" + toggle + " )" ); + assert.hasClasses( widget, "ui-classes-element-2", + "_" + method + "Class works with ( keys, null" + toggle + " )" ); + assert.hasClasses( widget, "ui-core-element-null", + "_" + method + "Class works with ( null, extra" + toggle + " )" ); + assert.hasClasses( widget.parent(), "ui-classes-widget ui-theme-widget", + "_" + method + "Class works with ( element, null, extra" + toggle + " )" ); + assert.hasClasses( widget.find( "span" ), "ui-classes-span ui-core-span", + "_" + method + "Class works with ( element, keys, extra" + toggle + " )" ); + assert.hasClasses( widget.find( "span" ), "ui-core-span-null", + "_" + method + "Class works with ( element, keys, null" + toggle + " )" ); +} +function elementLacksClasses( widget, method, assert ) { + var toggle = method === "toggle" ? ( ", false" ) : ""; + + assert.lacksClasses( widget, "ui-classes-element ui-theme-element ui-theme-element-2", + "_" + method + "Class works with ( keys, extra" + toggle + " )" ); + assert.lacksClasses( widget, "ui-classes-element-2", + "_" + method + "Class works with ( keys, null" + toggle + " )" ); + assert.lacksClasses( widget, "ui-core-element-null", + "_" + method + "Class works with ( null, extra" + toggle + " )" ); + assert.lacksClasses( widget.parent(), "ui-classes-widget ui-theme-widget", + "_" + method + "Class works with ( element, null, extra" + toggle + " )" ); + assert.lacksClasses( widget.find( "span" ), "ui-classes-span ui-core-span", + "_" + method + "Class works with ( element, keys, extra" + toggle + " )" ); + assert.lacksClasses( widget.find( "span" ), "ui-core-span-null", + "_" + method + "Class works with ( element, keys, null" + toggle + " )" ); +} + +test( ".option() - classes setter", function( assert ) { + expect( 11 ); + + var testWidget = $.ui.classesWidget(); + + elementHasClasses( testWidget.element, "add", assert ); + + testWidget.option({ + classes: { + "ui-classes-span": "custom-theme-span", + "ui-classes-widget": "ui-theme-widget custom-theme-widget", + "ui-classes-element": "ui-theme-element-2" + } + }); + + assert.lacksClasses( testWidget.element, "ui-theme-element", + "Removing a class from the value removes the class" ); + + testWidget.option( "classes.ui-classes-element", "" ); + assert.hasClasses( testWidget.element, "ui-classes-element", + "Setting to empty value leaves structure class" ); + assert.lacksClasses( testWidget.element, "ui-theme-element-2", + "Setting empty value removes previous value classes" ); + assert.hasClasses( testWidget.span, "ui-classes-span custom-theme-span", + "Adding a class to an empty value works as expected" ); + assert.hasClasses( testWidget.wrapper, "ui-classes-widget custom-theme-widget", + "Appending a class to the current value works as expected" ); +}); + +test( ".destroy() - class removal", function() { + expect( 1 ); + + domEqual( "#widget", function() { + $( "#widget" ).classesWidget().classesWidget( "destroy" ); + }); +}); + +test( "._add/_remove/_toggleClass()", function( assert ) { + expect( 24 ); + + var widget = $( "#widget" ).classesWidget(); + + elementHasClasses( widget, "add", assert ); + + widget.classesWidget( "toggleClasses", false ); + elementLacksClasses( widget, "toggle", assert ); + + widget.classesWidget( "toggleClasses", true ); + elementHasClasses( widget, "toggle", assert ); + + widget.classesWidget( "removeClasses" ); + elementLacksClasses( widget, "remove", assert ); +}); + +}( jQuery ) ); diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 2b88e39ef22..74d6af9da0a 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -227,6 +227,7 @@ test( "merge multiple option arguments", function() { $.widget( "ui.testWidget", { _create: function() { deepEqual( this.options, { + classes: {}, create: null, disabled: false, option1: "value1", @@ -281,6 +282,7 @@ test( "._getCreateOptions()", function() { }, _create: function() { deepEqual( this.options, { + classes: {}, create: null, disabled: false, option1: "override1", @@ -485,10 +487,11 @@ test( ".option() - getter", function() { options = div.testWidget( "option" ); deepEqual( options, { + baz: 5, + classes: {}, create: null, disabled: false, foo: "bar", - baz: 5, qux: [ "quux", "quuux" ] }, "full options hash returned" ); options.foo = "notbar"; @@ -980,7 +983,7 @@ test( "_off() - all events", function() { element.trigger( "bar" ); }); -test( "._hoverable()", function() { +test( "._hoverable()", function( assert ) { expect( 10 ); $.widget( "ui.testWidget", { _create: function() { @@ -989,30 +992,30 @@ test( "._hoverable()", function() { }); var div = $( "#widget" ).testWidget().children(); - ok( !div.hasClass( "ui-state-hover" ), "not hovered on init" ); + assert.lacksClasses( div, "ui-state-hover", "not hovered on init" ); div.trigger( "mouseenter" ); - ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + assert.hasClasses( div, "ui-state-hover", "hovered after mouseenter" ); div.trigger( "mouseleave" ); - ok( !div.hasClass( "ui-state-hover" ), "not hovered after mouseleave" ); + assert.lacksClasses( div, "ui-state-hover", "not hovered after mouseleave" ); div.trigger( "mouseenter" ); - ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + assert.hasClasses( div, "ui-state-hover", "hovered after mouseenter" ); $( "#widget" ).testWidget( "disable" ); - ok( !div.hasClass( "ui-state-hover" ), "not hovered while disabled" ); + assert.lacksClasses( div, "ui-state-hover", "not hovered while disabled" ); div.trigger( "mouseenter" ); - ok( !div.hasClass( "ui-state-hover" ), "can't hover while disabled" ); + assert.lacksClasses( div, "ui-state-hover", "can't hover while disabled" ); $( "#widget" ).testWidget( "enable" ); - ok( !div.hasClass( "ui-state-hover" ), "enabling doesn't reset hover" ); + assert.lacksClasses( div, "ui-state-hover", "enabling doesn't reset hover" ); div.trigger( "mouseenter" ); - ok( div.hasClass( "ui-state-hover" ), "hovered after mouseenter" ); + assert.hasClasses( div, "ui-state-hover", "hovered after mouseenter" ); $( "#widget" ).testWidget( "destroy" ); - ok( !div.hasClass( "ui-state-hover" ), "not hovered after destroy" ); + assert.lacksClasses( div, "ui-state-hover", "not hovered after destroy" ); div.trigger( "mouseenter" ); - ok( !div.hasClass( "ui-state-hover" ), "event handler removed on destroy" ); + assert.lacksClasses( div, "ui-state-hover", "event handler removed on destroy" ); }); -test( "._focusable()", function() { +test( "._focusable()", function( assert ) { expect( 10 ); $.widget( "ui.testWidget", { _create: function() { @@ -1021,27 +1024,27 @@ test( "._focusable()", function() { }); var div = $( "#widget" ).testWidget().children(); - ok( !div.hasClass( "ui-state-focus" ), "not focused on init" ); + assert.lacksClasses( div, "ui-state-focus", "not focused on init" ); div.trigger( "focusin" ); - ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + assert.hasClasses( div, "ui-state-focus", "focused after explicit focus" ); div.trigger( "focusout" ); - ok( !div.hasClass( "ui-state-focus" ), "not focused after blur" ); + assert.lacksClasses( div, "ui-state-focus", "not focused after blur" ); div.trigger( "focusin" ); - ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + assert.hasClasses( div, "ui-state-focus", "focused after explicit focus" ); $( "#widget" ).testWidget( "disable" ); - ok( !div.hasClass( "ui-state-focus" ), "not focused while disabled" ); + assert.lacksClasses( div, "ui-state-focus", "not focused while disabled" ); div.trigger( "focusin" ); - ok( !div.hasClass( "ui-state-focus" ), "can't focus while disabled" ); + assert.lacksClasses( div, "ui-state-focus", "can't focus while disabled" ); $( "#widget" ).testWidget( "enable" ); - ok( !div.hasClass( "ui-state-focus" ), "enabling doesn't reset focus" ); + assert.lacksClasses( div, "ui-state-focus", "enabling doesn't reset focus" ); div.trigger( "focusin" ); - ok( div.hasClass( "ui-state-focus" ), "focused after explicit focus" ); + assert.hasClasses( div, "ui-state-focus", "focused after explicit focus" ); $( "#widget" ).testWidget( "destroy" ); - ok( !div.hasClass( "ui-state-focus" ), "not focused after destroy" ); + assert.lacksClasses( div, "ui-state-focus", "not focused after destroy" ); div.trigger( "focusin" ); - ok( !div.hasClass( "ui-state-focus" ), "event handler removed on destroy" ); + assert.lacksClasses( div, "ui-state-focus", "event handler removed on destroy" ); }); test( "._trigger() - no event, no ui", function() { diff --git a/tests/visual/tooltip/tooltip.html b/tests/visual/tooltip/tooltip.html index 8d5364d783c..034021cd8ec 100644 --- a/tests/visual/tooltip/tooltip.html +++ b/tests/visual/tooltip/tooltip.html @@ -22,10 +22,14 @@ // custom class, replaces ui-widget-content $( "#context2" ).tooltip({ - tooltipClass: "ui-widget-header" + classes: { + "ui-tooltip": "ui-corner-all ui-widget-header" + } }); $( "#right1" ).tooltip({ - tooltipClass: "ui-state-error" + classes: { + "ui-tooltip": "ui-corner-all ui-state-error" + } }); // synchronous content @@ -63,7 +67,9 @@ // custom position $( "#right2" ).tooltip({ - tooltipClass: "ui-state-highlight", + classes: { + "ui-tooltip": "ui-corner-all ui-state-highlight" + }, position: { my: "center top", at: "center bottom+10" diff --git a/ui/accordion.js b/ui/accordion.js index 68a4c34ec0f..585137841ef 100644 --- a/ui/accordion.js +++ b/ui/accordion.js @@ -37,6 +37,11 @@ return $.widget( "ui.accordion", { options: { active: 0, animate: {}, + classes: { + "ui-accordion-header": "ui-corner-top", + "ui-accordion-header-collapsed": "ui-corner-all", + "ui-accordion-content": "ui-corner-bottom" + }, collapsible: false, event: "click", header: "> li > :first-child,> :not(li):even", @@ -69,10 +74,10 @@ return $.widget( "ui.accordion", { _create: function() { var options = this.options; + this.prevShow = this.prevHide = $(); - this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) - // ARIA - .attr( "role", "tablist" ); + this._addClass( "ui-accordion", "ui-widget ui-helper-reset" ); + this.element.attr( "role", "tablist" ); // don't allow collapsible: false and active: false / null if ( !options.collapsible && (options.active === false || options.active == null) ) { @@ -95,37 +100,33 @@ return $.widget( "ui.accordion", { }, _createIcons: function() { - var icons = this.options.icons; + var icon, children, + icons = this.options.icons; + if ( icons ) { - $( "" ) - .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) - .prependTo( this.headers ); - this.active.children( ".ui-accordion-header-icon" ) - .removeClass( icons.header ) - .addClass( icons.activeHeader ); - this.headers.addClass( "ui-accordion-icons" ); + icon = $( "" ); + this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header ); + icon.prependTo( this.headers ); + children = this.active.children( ".ui-accordion-header-icon" ); + this._removeClass( children, icons.header ) + ._addClass( children, null, icons.activeHeader ) + ._addClass( this.headers, "ui-accordion-icons" ); } }, _destroyIcons: function() { - this.headers - .removeClass( "ui-accordion-icons" ) - .children( ".ui-accordion-header-icon" ) - .remove(); + this._removeClass( this.headers, "ui-accordion-icons" ); + this.headers.children( ".ui-accordion-header-icon" ).remove(); }, _destroy: function() { var contents; // clean up main element - this.element - .removeClass( "ui-accordion ui-widget ui-helper-reset" ) - .removeAttr( "role" ); + this.element.removeAttr( "role" ); // clean up headers this.headers - .removeClass( "ui-accordion-header ui-accordion-header-active ui-state-default " + - "ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) .removeAttr( "role" ) .removeAttr( "aria-expanded" ) .removeAttr( "aria-selected" ) @@ -137,8 +138,6 @@ return $.widget( "ui.accordion", { // clean up content panels contents = this.headers.next() - .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom " + - "ui-accordion-content ui-accordion-content-active ui-state-disabled" ) .css( "display", "" ) .removeAttr( "role" ) .removeAttr( "aria-hidden" ) @@ -178,14 +177,15 @@ return $.widget( "ui.accordion", { } } - // #5332 - opacity doesn't cascade to positioned elements in IE + // Support: IE8 Only + // #5332 / #6059 - opacity doesn't cascade to positioned elements in IE // so we need to add the disabled class to the headers and panels if ( key === "disabled" ) { - this.element - .toggleClass( "ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.headers.add( this.headers.next() ) - .toggleClass( "ui-state-disabled", !!value ); + this.element.attr( "aria-disabled", value ); + + this._toggleClass( null, "ui-state-disabled", !!value ); + this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled", + !!value ); } }, @@ -270,13 +270,12 @@ return $.widget( "ui.accordion", { var prevHeaders = this.headers, prevPanels = this.panels; - this.headers = this.element.find( this.options.header ) - .addClass( "ui-accordion-header ui-state-default ui-corner-all" ); + this.headers = this.element.find( this.options.header ); + this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed", + "ui-state-default" ); - this.panels = this.headers.next() - .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) - .filter( ":not(.ui-accordion-content-active)" ) - .hide(); + this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide(); + this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" ); // Avoid memory leaks (#10056) if ( prevPanels ) { @@ -291,12 +290,11 @@ return $.widget( "ui.accordion", { heightStyle = options.heightStyle, parent = this.element.parent(); - this.active = this._findActive( options.active ) - .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) - .removeClass( "ui-corner-all" ); - this.active.next() - .addClass( "ui-accordion-content-active" ) - .show(); + this.active = this._findActive( options.active ); + this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" ) + ._removeClass( this.active, "ui-accordion-header-collapsed" ); + this._addClass( this.active.next(), "ui-accordion-content-active" ); + this.active.next().show(); this.headers .attr( "role", "tab" ) @@ -415,7 +413,8 @@ return $.widget( "ui.accordion", { }, _eventHandler: function( event ) { - var options = this.options, + var activeChildren, clickedChildren, + options = this.options, active = this.active, clicked = $( event.currentTarget ), clickedIsActive = clicked[ 0 ] === active[ 0 ], @@ -448,26 +447,23 @@ return $.widget( "ui.accordion", { // switch classes // corner classes on the previously active header stay after the animation - active.removeClass( "ui-accordion-header-active ui-state-active" ); + this._removeClass( active, "ui-accordion-header-active", "ui-state-active" ); if ( options.icons ) { - active.children( ".ui-accordion-header-icon" ) - .removeClass( options.icons.activeHeader ) - .addClass( options.icons.header ); + activeChildren = active.children( ".ui-accordion-header-icon" ); + this._removeClass( activeChildren, null, options.icons.activeHeader ) + ._addClass( activeChildren, null, options.icons.header ); } if ( !clickedIsActive ) { - clicked - .removeClass( "ui-corner-all" ) - .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); + this._removeClass( clicked, "ui-accordion-header-collapsed" ) + ._addClass( clicked, "ui-accordion-header-active", "ui-state-active" ); if ( options.icons ) { - clicked.children( ".ui-accordion-header-icon" ) - .removeClass( options.icons.header ) - .addClass( options.icons.activeHeader ); + clickedChildren = clicked.children( ".ui-accordion-header-icon" ); + this._removeClass( clickedChildren, null, options.icons.header ) + ._addClass( clickedChildren, null, options.icons.activeHeader ); } - clicked - .next() - .addClass( "ui-accordion-content-active" ); + this._addClass( clicked.next(), "ui-accordion-content-active" ); } }, @@ -579,13 +575,12 @@ return $.widget( "ui.accordion", { }, _toggleComplete: function( data ) { - var toHide = data.oldPanel; + var toHide = data.oldPanel, + prev = toHide.prev(); - toHide - .removeClass( "ui-accordion-content-active" ) - .prev() - .removeClass( "ui-corner-top" ) - .addClass( "ui-corner-all" ); + this._removeClass( toHide, "ui-accordion-content-active" ); + this._removeClass( prev, "ui-accordion-header-active" ) + ._addClass( prev, "ui-accordion-header-collapsed" ); // Work around for rendering bug in IE (#5421) if ( toHide.length ) { diff --git a/ui/autocomplete.js b/ui/autocomplete.js index 60d69307edb..3453ee39948 100644 --- a/ui/autocomplete.js +++ b/ui/autocomplete.js @@ -40,6 +40,7 @@ $.widget( "ui.autocomplete", { options: { appendTo: null, autoFocus: false, + classes: {}, delay: 300, minLength: 1, position: { @@ -87,9 +88,8 @@ $.widget( "ui.autocomplete", { this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; this.isNewMenu = true; - this.element - .addClass( "ui-autocomplete-input" ) - .attr( "autocomplete", "off" ); + this._addClass( "ui-autocomplete-input" ); + this.element.attr( "autocomplete", "off" ); this._on( this.element, { keydown: function( event ) { @@ -210,7 +210,6 @@ $.widget( "ui.autocomplete", { this._initSource(); this.menu = $( "
    " ) - .addClass( "ui-autocomplete ui-front" ) .appendTo( this._appendTo() ) .menu({ // disable ARIA support, the live region takes care of that @@ -219,6 +218,7 @@ $.widget( "ui.autocomplete", { .hide() .menu( "instance" ); + this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); this._on( this.menu.element, { mousedown: function( event ) { // prevent moving focus out of the text field @@ -315,9 +315,10 @@ $.widget( "ui.autocomplete", { "aria-live": "assertive", "aria-relevant": "additions" }) - .addClass( "ui-helper-hidden-accessible" ) .appendTo( this.document[ 0 ].body ); + this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); + // turning off autocomplete prevents the browser from remembering the // value when navigating through history, so we re-enable autocomplete // if the page is unloaded before the widget is destroyed. #7790 @@ -330,9 +331,7 @@ $.widget( "ui.autocomplete", { _destroy: function() { clearTimeout( this.searching ); - this.element - .removeClass( "ui-autocomplete-input" ) - .removeAttr( "autocomplete" ); + this.element.removeAttr( "autocomplete" ); this.menu.element.remove(); this.liveRegion.remove(); }, @@ -436,7 +435,7 @@ $.widget( "ui.autocomplete", { _search: function( value ) { this.pending++; - this.element.addClass( "ui-autocomplete-loading" ); + this._addClass( "ui-autocomplete-loading" ); this.cancelSearch = false; this.source( { term: value }, this._response() ); @@ -452,7 +451,7 @@ $.widget( "ui.autocomplete", { this.pending--; if ( !this.pending ) { - this.element.removeClass( "ui-autocomplete-loading" ); + this._removeClass( "ui-autocomplete-loading" ); } }, this ); }, diff --git a/ui/dialog.js b/ui/dialog.js index 0775fbe8df9..3eecde43f27 100644 --- a/ui/dialog.js +++ b/ui/dialog.js @@ -37,15 +37,18 @@ } }(function( $ ) { -return $.widget( "ui.dialog", { +$.widget( "ui.dialog", { version: "@VERSION", options: { appendTo: "body", autoOpen: true, buttons: [], + classes: { + "ui-dialog": "ui-corner-all", + "ui-dialog-titlebar": "ui-corner-all" + }, closeOnEscape: true, closeText: "Close", - dialogClass: "", draggable: true, hide: null, height: "auto", @@ -122,9 +125,10 @@ return $.widget( "ui.dialog", { this.element .show() .removeAttr( "title" ) - .addClass( "ui-dialog-content ui-widget-content" ) .appendTo( this.uiDialog ); + this._addClass( "ui-dialog-content", "ui-widget-content" ); + this._createTitlebar(); this._createButtonPane(); @@ -163,7 +167,6 @@ return $.widget( "ui.dialog", { this.element .removeUniqueId() - .removeClass( "ui-dialog-content ui-widget-content" ) .css( this.originalCss ) // Without detaching first, the following becomes really slow .detach(); @@ -336,8 +339,6 @@ return $.widget( "ui.dialog", { _createWrapper: function() { this.uiDialog = $("
    ") - .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " + - this.options.dialogClass ) .hide() .attr({ // Setting tabIndex makes the div focusable @@ -346,6 +347,7 @@ return $.widget( "ui.dialog", { }) .appendTo( this._appendTo() ); + this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" ); this._on( this.uiDialog, { keydown: function( event ) { if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode && @@ -395,9 +397,9 @@ return $.widget( "ui.dialog", { _createTitlebar: function() { var uiDialogTitle; - this.uiDialogTitlebar = $( "
    " ) - .addClass( "ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix" ) - .prependTo( this.uiDialog ); + this.uiDialogTitlebar = $( "
    " ); + this._addClass( this.uiDialogTitlebar, + "ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" ); this._on( this.uiDialogTitlebar, { mousedown: function( event ) { // Don't prevent click on close button (#8838) @@ -421,8 +423,9 @@ return $.widget( "ui.dialog", { }, text: false }) - .addClass( "ui-dialog-titlebar-close" ) .appendTo( this.uiDialogTitlebar ); + + this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" ); this._on( this.uiDialogTitlebarClose, { click: function( event ) { event.preventDefault(); @@ -430,12 +433,12 @@ return $.widget( "ui.dialog", { } }); - uiDialogTitle = $( "" ) - .uniqueId() - .addClass( "ui-dialog-title" ) - .prependTo( this.uiDialogTitlebar ); + uiDialogTitle = $( "" ).uniqueId().prependTo( this.uiDialogTitlebar ); + this._addClass( uiDialogTitle, "ui-dialog-title" ); this._title( uiDialogTitle ); + this.uiDialogTitlebar.prependTo( this.uiDialog ); + this.uiDialog.attr({ "aria-labelledby": uiDialogTitle.attr( "id" ) }); @@ -449,12 +452,13 @@ return $.widget( "ui.dialog", { }, _createButtonPane: function() { - this.uiDialogButtonPane = $( "
    " ) - .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" ); + this.uiDialogButtonPane = $( "
    " ); + this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane", + "ui-widget-content ui-helper-clearfix" ); this.uiButtonSet = $( "
    " ) - .addClass( "ui-dialog-buttonset" ) .appendTo( this.uiDialogButtonPane ); + this._addClass( this.uiButtonSet, "ui-dialog-buttonset" ); this._createButtons(); }, @@ -468,7 +472,7 @@ return $.widget( "ui.dialog", { this.uiButtonSet.empty(); if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) { - this.uiDialog.removeClass( "ui-dialog-buttons" ); + this._removeClass( this.uiDialog, "ui-dialog-buttons" ); return; } @@ -494,7 +498,7 @@ return $.widget( "ui.dialog", { .button( buttonOptions ) .appendTo( that.uiButtonSet ); }); - this.uiDialog.addClass( "ui-dialog-buttons" ); + this._addClass( this.uiDialog, "ui-dialog-buttons" ); this.uiDialogButtonPane.appendTo( this.uiDialog ); }, @@ -514,7 +518,7 @@ return $.widget( "ui.dialog", { handle: ".ui-dialog-titlebar", containment: "document", start: function( event, ui ) { - $( this ).addClass( "ui-dialog-dragging" ); + that._addClass( $( this ), "ui-dialog-dragging" ); that._blockFrames(); that._trigger( "dragStart", event, filteredUi( ui ) ); }, @@ -531,7 +535,7 @@ return $.widget( "ui.dialog", { "top" + (top >= 0 ? "+" : "") + top, of: that.window }; - $( this ).removeClass( "ui-dialog-dragging" ); + that._removeClass( $( this ), "ui-dialog-dragging" ); that._unblockFrames(); that._trigger( "dragStop", event, filteredUi( ui ) ); } @@ -568,7 +572,7 @@ return $.widget( "ui.dialog", { minHeight: this._minHeight(), handles: resizeHandles, start: function( event, ui ) { - $( this ).addClass( "ui-dialog-resizing" ); + that._addClass( $( this ), "ui-dialog-resizing" ); that._blockFrames(); that._trigger( "resizeStart", event, filteredUi( ui ) ); }, @@ -588,7 +592,7 @@ return $.widget( "ui.dialog", { "top" + (top >= 0 ? "+" : "") + top, of: that.window }; - $( this ).removeClass( "ui-dialog-resizing" ); + that._removeClass( $( this ), "ui-dialog-resizing" ); that._unblockFrames(); that._trigger( "resizeStop", event, filteredUi( ui ) ); } @@ -676,12 +680,6 @@ return $.widget( "ui.dialog", { var isDraggable, isResizable, uiDialog = this.uiDialog; - if ( key === "dialogClass" ) { - uiDialog - .removeClass( this.options.dialogClass ) - .addClass( value ); - } - if ( key === "disabled" ) { return; } @@ -850,8 +848,9 @@ return $.widget( "ui.dialog", { } this.overlay = $( "
    " ) - .addClass( "ui-widget-overlay ui-front" ) .appendTo( this._appendTo() ); + + this._addClass( this.overlay, null, "ui-widget-overlay ui-front" ); this._on( this.overlay, { mousedown: "_keepFocus" }); @@ -881,4 +880,30 @@ return $.widget( "ui.dialog", { } }); +// DEPRECATED +// TODO: switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for dialogClass option + $.widget( "ui.dialog", $.ui.dialog, { + options: { + dialogClass: "" + }, + _createWrapper: function() { + this._super(); + this.uiDialog.addClass( this.options.dialogClass ); + }, + _setOption: function( key, value ) { + if ( key === "dialogClass" ) { + this.uiDialog + .removeClass( this.options.dialogClass ) + .addClass( value ); + } + this._superApply( arguments ); + } + }); +} + +return $.ui.dialog; + })); diff --git a/ui/draggable.js b/ui/draggable.js index c57a3517c98..83ad6487a55 100644 --- a/ui/draggable.js +++ b/ui/draggable.js @@ -71,10 +71,10 @@ $.widget("ui.draggable", $.ui.mouse, { this._setPositionRelative(); } if (this.options.addClasses){ - this.element.addClass("ui-draggable"); + this._addClass( "ui-draggable" ); } if (this.options.disabled){ - this.element.addClass("ui-draggable-disabled"); + this._addClass( "ui-draggable-disabled" ); } this._setHandleClassName(); @@ -94,7 +94,6 @@ $.widget("ui.draggable", $.ui.mouse, { this.destroyOnClear = true; return; } - this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); this._removeHandleClassName(); this._mouseDestroy(); }, @@ -170,7 +169,7 @@ $.widget("ui.draggable", $.ui.mouse, { //Create and append the visible helper this.helper = this._createHelper(event); - this.helper.addClass("ui-draggable-dragging"); + this._addClass( this.helper, "ui-draggable-dragging" ); //Cache the helper size this._cacheHelperProportions(); @@ -352,11 +351,11 @@ $.widget("ui.draggable", $.ui.mouse, { _setHandleClassName: function() { this.handleElement = this.options.handle ? this.element.find( this.options.handle ) : this.element; - this.handleElement.addClass( "ui-draggable-handle" ); + this._addClass( this.handleElement, "ui-draggable-handle" ); }, _removeHandleClassName: function() { - this.handleElement.removeClass( "ui-draggable-handle" ); + this._removeClass( this.handleElement, "ui-draggable-handle" ); }, _createHelper: function(event) { @@ -660,7 +659,7 @@ $.widget("ui.draggable", $.ui.mouse, { }, _clear: function() { - this.helper.removeClass("ui-draggable-dragging"); + this._removeClass( this.helper, "ui-draggable-dragging" ); if (this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) { this.helper.remove(); } diff --git a/ui/droppable.js b/ui/droppable.js index a164f5522b6..98be34bab28 100644 --- a/ui/droppable.js +++ b/ui/droppable.js @@ -36,10 +36,8 @@ $.widget( "ui.droppable", { widgetEventPrefix: "drop", options: { accept: "*", - activeClass: false, addClasses: true, greedy: false, - hoverClass: false, scope: "default", tolerance: "intersect", @@ -80,7 +78,7 @@ $.widget( "ui.droppable", { this._addToManager( o.scope ); - o.addClasses && this.element.addClass( "ui-droppable" ); + o.addClasses && this._addClass( "ui-droppable" ); }, @@ -103,8 +101,6 @@ $.widget( "ui.droppable", { var drop = $.ui.ddmanager.droppables[ this.options.scope ]; this._splice( drop ); - - this.element.removeClass( "ui-droppable ui-droppable-disabled" ); }, _setOption: function( key, value ) { @@ -125,9 +121,8 @@ $.widget( "ui.droppable", { _activate: function( event ) { var draggable = $.ui.ddmanager.current; - if ( this.options.activeClass ) { - this.element.addClass( this.options.activeClass ); - } + + this._addActiveClass(); if ( draggable ){ this._trigger( "activate", event, this.ui( draggable ) ); } @@ -135,9 +130,8 @@ $.widget( "ui.droppable", { _deactivate: function( event ) { var draggable = $.ui.ddmanager.current; - if ( this.options.activeClass ) { - this.element.removeClass( this.options.activeClass ); - } + + this._removeActiveClass(); if ( draggable ){ this._trigger( "deactivate", event, this.ui( draggable ) ); } @@ -153,9 +147,7 @@ $.widget( "ui.droppable", { } if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) { - if ( this.options.hoverClass ) { - this.element.addClass( this.options.hoverClass ); - } + this._addHoverClass(); this._trigger( "over", event, this.ui( draggable ) ); } @@ -171,9 +163,7 @@ $.widget( "ui.droppable", { } if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) { - if ( this.options.hoverClass ) { - this.element.removeClass( this.options.hoverClass ); - } + this._removeHoverClass(); this._trigger( "out", event, this.ui( draggable ) ); } @@ -204,12 +194,9 @@ $.widget( "ui.droppable", { } if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) { - if ( this.options.activeClass ) { - this.element.removeClass( this.options.activeClass ); - } - if ( this.options.hoverClass ) { - this.element.removeClass( this.options.hoverClass ); - } + this._removeActiveClass(); + this._removeHoverClass(); + this._trigger( "drop", event, this.ui( draggable ) ); return this.element; } @@ -225,8 +212,25 @@ $.widget( "ui.droppable", { position: c.position, offset: c.positionAbs }; - } + }, + // Extension points just to make backcompat sane and avoid duplicating logic + // TODO: Remove in 1.13 along with call to it below + _addHoverClass: function() { + this._addClass( "ui-droppable-hover" ); + }, + + _removeHoverClass: function() { + this._removeClass( "ui-droppable-hover" ); + }, + + _addActiveClass: function() { + this._addClass( "ui-droppable-active" ); + }, + + _removeActiveClass: function() { + this._removeClass( "ui-droppable-active" ); + } }); var intersect = (function() { @@ -413,6 +417,43 @@ $.ui.ddmanager = { } }; +// DEPRECATED +// TODO: switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for activeClass and hoverClass options + $.widget( "ui.droppable", $.ui.droppable, { + options: { + hoverClass: false, + activeClass: false + }, + _addActiveClass: function() { + this._super(); + if ( this.options.activeClass ) { + this.element.addClass( this.options.activeClass ); + } + }, + _removeActiveClass: function() { + this._super(); + if ( this.options.activeClass ) { + this.element.removeClass( this.options.activeClass ); + } + }, + _addHoverClass: function() { + this._super(); + if ( this.options.hoverClass ) { + this.element.addClass( this.options.hoverClass ); + } + }, + _removeHoverClass: function() { + this._super(); + if ( this.options.hoverClass ) { + this.element.removeClass( this.options.hoverClass ); + } + } + }); +} + return $.ui.droppable; })); diff --git a/ui/menu.js b/ui/menu.js index 353368cf959..6c09e336f5f 100644 --- a/ui/menu.js +++ b/ui/menu.js @@ -38,6 +38,7 @@ return $.widget( "ui.menu", { defaultElement: "
      ", delay: 300, options: { + classes: {}, icons: { submenu: "ui-icon-caret-1-e" }, @@ -63,20 +64,19 @@ return $.widget( "ui.menu", { this.mouseHandled = false; this.element .uniqueId() - .addClass( "ui-menu ui-widget ui-widget-content" ) - .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ) .attr({ role: this.options.role, tabIndex: 0 }); if ( this.options.disabled ) { - this.element - .addClass( "ui-state-disabled" ) - .attr( "aria-disabled", "true" ); + this._addClass( null, "ui-state-disabled" ); + this.element.attr( "aria-disabled", "true" ); } + this._addClass( "ui-menu", "ui-widget ui-widget-content" ); this._on({ + // Prevent focus from sticking to links inside menu after clicking // them (focus should always stay on UL during navigation). "mousedown .ui-menu-item": function( event ) { @@ -118,8 +118,8 @@ return $.widget( "ui.menu", { var target = $( event.currentTarget ); // Remove ui-state-active class from siblings of the newly focused menu item // to avoid a jump caused by adjacent elements both having a class with a border - target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); - + this._removeClass( target.siblings().children( ".ui-state-active" ), + null, "ui-state-active" ); this.focus( event, target ); }, mouseleave: "collapseAll", @@ -159,11 +159,19 @@ return $.widget( "ui.menu", { }, _destroy: function() { + var items = this.element.find( ".ui-menu-item" ) + .removeAttr( "role" ) + .removeAttr( "aria-disabled" ), + submenus = items.children( ".ui-menu-item-wrapper" ) + .removeUniqueId() + .removeAttr( "tabIndex" ) + .removeAttr( "role" ) + .removeAttr( "aria-haspopup" ); + // Destroy (sub)menus this.element .removeAttr( "aria-activedescendant" ) .find( ".ui-menu" ).addBack() - .removeClass( "ui-menu ui-widget ui-widget-content ui-menu-icons ui-front" ) .removeAttr( "role" ) .removeAttr( "tabIndex" ) .removeAttr( "aria-labelledby" ) @@ -173,26 +181,12 @@ return $.widget( "ui.menu", { .removeUniqueId() .show(); - // Destroy menu items - this.element.find( ".ui-menu-item" ) - .removeClass( "ui-menu-item" ) - .removeAttr( "role" ) - .removeAttr( "aria-disabled" ) - .children( ".ui-menu-item-wrapper" ) - .removeUniqueId() - .removeClass( "ui-menu-item-wrapper ui-state-hover" ) - .removeAttr( "tabIndex" ) - .removeAttr( "role" ) - .removeAttr( "aria-haspopup" ) - .children().each(function() { - var elem = $( this ); - if ( elem.data( "ui-menu-submenu-caret" ) ) { - elem.remove(); - } - }); - - // Destroy menu dividers - this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); + submenus.children().each(function() { + var elem = $( this ); + if ( elem.data( "ui-menu-submenu-caret" ) ) { + elem.remove(); + } + }); }, _keydown: function( event ) { @@ -286,16 +280,15 @@ return $.widget( "ui.menu", { }, refresh: function() { - var menus, items, + var menus, items, newSubmenus, newItems, newWrappers, that = this, icon = this.options.icons.submenu, submenus = this.element.find( this.options.menus ); - this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ); + this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length ); // Initialize nested menus - submenus.filter( ":not(.ui-menu)" ) - .addClass( "ui-menu ui-widget ui-widget-content ui-front" ) + newSubmenus = submenus.filter( ":not(.ui-menu)" ) .hide() .attr({ role: this.options.role, @@ -305,16 +298,17 @@ return $.widget( "ui.menu", { .each(function() { var menu = $( this ), item = menu.prev(), - submenuCaret = $( "" ) - .addClass( "ui-menu-icon ui-icon " + icon ) - .data( "ui-menu-submenu-caret", true ); + submenuCaret = $( "" ).data( "ui-menu-submenu-caret", true ); + that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon ); item .attr( "aria-haspopup", "true" ) .prepend( submenuCaret ); menu.attr( "aria-labelledby", item.attr( "id" ) ); }); + this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" ); + menus = submenus.add( this.element ); items = menus.find( this.options.items ); @@ -322,21 +316,21 @@ return $.widget( "ui.menu", { items.not( ".ui-menu-item" ).each(function() { var item = $( this ); if ( that._isDivider( item ) ) { - item.addClass( "ui-widget-content ui-menu-divider" ); + that._addClass( item, "ui-menu-divider", "ui-widget-content" ); } }); // Don't refresh list items that are already adapted - items.not( ".ui-menu-item, .ui-menu-divider" ) - .addClass( "ui-menu-item" ) - .children() - .not( ".ui-menu" ) - .addClass( "ui-menu-item-wrapper" ) - .uniqueId() - .attr({ - tabIndex: -1, - role: this._itemRole() - }); + newItems = items.not( ".ui-menu-item, .ui-menu-divider" ); + newWrappers = newItems.children() + .not( ".ui-menu" ) + .uniqueId() + .attr({ + tabIndex: -1, + role: this._itemRole() + }); + this._addClass( newItems, "ui-menu-item" ) + ._addClass( newWrappers, "ui-menu-item-wrapper" ); // Add aria-disabled attribute to any disabled menu item items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); @@ -356,26 +350,27 @@ return $.widget( "ui.menu", { _setOption: function( key, value ) { if ( key === "icons" ) { - this.element.find( ".ui-menu-icon" ) - .removeClass( this.options.icons.submenu ) - .addClass( value.submenu ); + var icons = this.element.find( ".ui-menu-icon" ); + this._removeClass( icons, null, this.options.icons.submenu ) + ._addClass( icons, null, value.submenu ); } if ( key === "disabled" ) { - this.element - .toggleClass( "ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); + this.element.attr( "aria-disabled", value ); + this._toggleClass( null, "ui-state-disabled", !!value ); } this._super( key, value ); }, focus: function( event, item ) { - var nested, focused; + var nested, focused, activeParent; this.blur( event, event && event.type === "focus" ); this._scrollIntoView( item ); this.active = item.first(); - focused = this.active.children( ".ui-menu-item-wrapper" ).addClass( "ui-state-active" ); + + focused = this.active.children( ".ui-menu-item-wrapper" ); + this._addClass( focused, null, "ui-state-active" ); // Only update aria-activedescendant if there's a role // otherwise we assume focus is managed elsewhere @@ -384,11 +379,11 @@ return $.widget( "ui.menu", { } // Highlight active parent menu item, if any - this.active + activeParent = this.active .parent() .closest( ".ui-menu-item" ) - .children( ".ui-menu-item-wrapper" ) - .addClass( "ui-state-active" ); + .children( ".ui-menu-item-wrapper" ); + this._addClass( activeParent, null, "ui-state-active" ); if ( event && event.type === "keydown" ) { this._close(); @@ -434,7 +429,8 @@ return $.widget( "ui.menu", { return; } - this.active.children( ".ui-menu-item-wrapper" ).removeClass( "ui-state-active" ); + this._removeClass( this.active.children( ".ui-menu-item-wrapper" ), + null, "ui-state-active" ); this.active = null; this._trigger( "blur", event, { item: this.active } ); @@ -498,14 +494,14 @@ return $.widget( "ui.menu", { startMenu = this.active ? this.active.parent() : this.element; } - startMenu + var active = startMenu .find( ".ui-menu" ) .hide() .attr( "aria-hidden", "true" ) .attr( "aria-expanded", "false" ) .end() - .find( ".ui-state-active" ).not( ".ui-menu-item-wrapper" ) - .removeClass( "ui-state-active" ); + .find( ".ui-state-active" ).not( ".ui-menu-item-wrapper" ); + this._removeClass( active, null, "ui-state-active" ); }, _closeOnDocumentClick: function( event ) { diff --git a/ui/progressbar.js b/ui/progressbar.js index 28912d975e8..d6127950eb4 100644 --- a/ui/progressbar.js +++ b/ui/progressbar.js @@ -35,6 +35,11 @@ return $.widget( "ui.progressbar", { version: "@VERSION", options: { + classes: { + "ui-progressbar": "ui-corner-all", + "ui-progressbar-value": "ui-corner-left", + "ui-progressbar-complete": "ui-corner-right" + }, max: 100, value: 0, @@ -45,27 +50,26 @@ return $.widget( "ui.progressbar", { min: 0, _create: function() { + // Constrain initial value this.oldValue = this.options.value = this._constrainedValue(); - this.element - .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) - .attr({ - // Only set static values, aria-valuenow and aria-valuemax are - // set inside _refreshValue() - role: "progressbar", - "aria-valuemin": this.min - }); + this.element.attr({ - this.valueDiv = $( "
      " ) - .appendTo( this.element ); + // Only set static values; aria-valuenow and aria-valuemax are + // set inside _refreshValue() + role: "progressbar", + "aria-valuemin": this.min + }); + this._addClass( "ui-progressbar", "ui-widget ui-widget-content" ); + this.valueDiv = $( "
      " ).appendTo( this.element ); + this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" ); this._refreshValue(); }, _destroy: function() { this.element - .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" ) .removeAttr( "role" ) .removeAttr( "aria-valuemin" ) .removeAttr( "aria-valuemax" ) @@ -116,9 +120,8 @@ return $.widget( "ui.progressbar", { value = Math.max( this.min, value ); } if ( key === "disabled" ) { - this.element - .toggleClass( "ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); + this.element.attr( "aria-disabled", value ); + this._toggleClass( null, "ui-state-disabled", !!value ); } this._super( key, value ); }, @@ -133,15 +136,18 @@ return $.widget( "ui.progressbar", { this.valueDiv .toggle( this.indeterminate || value > this.min ) - .toggleClass( "ui-corner-right", value === this.options.max ) .width( percentage.toFixed(0) + "%" ); - this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate ); + this + ._toggleClass( this.valueDiv, "ui-progressbar-complete", null, + value === this.options.max ) + ._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate ); if ( this.indeterminate ) { this.element.removeAttr( "aria-valuenow" ); if ( !this.overlayDiv ) { - this.overlayDiv = $( "
      " ).appendTo( this.valueDiv ); + this.overlayDiv = $( "
      " ).appendTo( this.valueDiv ); + this._addClass( this.overlayDiv, "ui-progressbar-overlay" ); } } else { this.element.attr({ diff --git a/ui/resizable.js b/ui/resizable.js index 09efc9a37d6..7aadd490cbf 100644 --- a/ui/resizable.js +++ b/ui/resizable.js @@ -43,6 +43,9 @@ $.widget("ui.resizable", $.ui.mouse, { animateEasing: "swing", aspectRatio: false, autoHide: false, + classes: { + "ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se" + }, containment: false, ghost: false, grid: false, @@ -96,7 +99,7 @@ $.widget("ui.resizable", $.ui.mouse, { var n, i, handle, axis, hname, that = this, o = this.options; - this.element.addClass("ui-resizable"); + this._addClass( "ui-resizable" ); $.extend(this, { _aspectRatio: !!(o.aspectRatio), @@ -182,15 +185,11 @@ $.widget("ui.resizable", $.ui.mouse, { handle = $.trim(n[i]); hname = "ui-resizable-" + handle; - axis = $("
      "); + axis = $("
      "); + this._addClass( axis, "ui-resizable-handle " + hname ); axis.css({ zIndex: o.zIndex }); - // TODO : What's going on here? - if ("se" === handle) { - axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se"); - } - this.handles[handle] = ".ui-resizable-" + handle; this.element.append(axis); } @@ -249,13 +248,13 @@ $.widget("ui.resizable", $.ui.mouse, { if (o.autoHide) { this._handles.hide(); + this._addClass( "ui-resizable-autohide" ); $(this.element) - .addClass("ui-resizable-autohide") .mouseenter(function() { if (o.disabled) { return; } - $(this).removeClass("ui-resizable-autohide"); + that._removeClass( "ui-resizable-autohide" ); that._handles.show(); }) .mouseleave(function() { @@ -263,7 +262,7 @@ $.widget("ui.resizable", $.ui.mouse, { return; } if (!that.resizing) { - $(this).addClass("ui-resizable-autohide"); + that._addClass( "ui-resizable-autohide" ); that._handles.hide(); } }); @@ -279,7 +278,6 @@ $.widget("ui.resizable", $.ui.mouse, { var wrapper, _destroy = function(exp) { $(exp) - .removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") .removeData("resizable") .removeData("ui-resizable") .unbind(".resizable") @@ -373,7 +371,7 @@ $.widget("ui.resizable", $.ui.mouse, { cursor = $(".ui-resizable-" + this.axis).css("cursor"); $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor); - el.addClass("ui-resizable-resizing"); + this._addClass( "ui-resizable-resizing" ); this._propagate("start", event); return true; }, @@ -457,7 +455,7 @@ $.widget("ui.resizable", $.ui.mouse, { $("body").css("cursor", "auto"); - this.element.removeClass("ui-resizable-resizing"); + this._removeClass( "ui-resizable-resizing" ); this._propagate("stop", event); @@ -686,7 +684,8 @@ $.widget("ui.resizable", $.ui.mouse, { this.helper = this.helper || $("
      "); - this.helper.addClass(this._helper).css({ + this._addClass( this.helper, this._helper ); + this.helper.css({ width: this.element.outerWidth() - 1, height: this.element.outerHeight() - 1, position: "absolute", @@ -1040,22 +1039,29 @@ $.ui.plugin.add("resizable", "ghost", { start: function() { - var that = $(this).resizable( "instance" ), o = that.options, cs = that.size; + var that = $(this).resizable( "instance" ), cs = that.size; that.ghost = that.originalElement.clone(); - that.ghost - .css({ - opacity: 0.25, - display: "block", - position: "relative", - height: cs.height, - width: cs.width, - margin: 0, - left: 0, - top: 0 - }) - .addClass("ui-resizable-ghost") - .addClass(typeof o.ghost === "string" ? o.ghost : ""); + that.ghost.css({ + opacity: 0.25, + display: "block", + position: "relative", + height: cs.height, + width: cs.width, + margin: 0, + left: 0, + top: 0 + }); + + that._addClass( that.ghost, "ui-resizable-ghost" ); + + // DEPRECATED + // TODO: remove after 1.12 + if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) { + + // Ghost option + that.ghost.addClass( this.options.ghost ); + } that.ghost.appendTo(that.helper); diff --git a/ui/selectable.js b/ui/selectable.js index 9c8815fb658..c1d4459546d 100644 --- a/ui/selectable.js +++ b/ui/selectable.js @@ -49,18 +49,17 @@ return $.widget("ui.selectable", $.ui.mouse, { unselecting: null }, _create: function() { - var selectees, - that = this; + var that = this; - this.element.addClass("ui-selectable"); + this._addClass( "ui-selectable" ); this.dragged = false; // cache selectee children based on filter this.refresh = function() { - selectees = $(that.options.filter, that.element[0]); - selectees.addClass("ui-selectee"); - selectees.each(function() { + that.selectees = $(that.options.filter, that.element[0]); + that._addClass( that.selectees, "ui-selectee" ); + that.selectees.each(function() { var $this = $(this), pos = $this.offset(); $.data(this, "selectable-item", { @@ -79,19 +78,14 @@ return $.widget("ui.selectable", $.ui.mouse, { }; this.refresh(); - this.selectees = selectees.addClass("ui-selectee"); - this._mouseInit(); - this.helper = $("
      "); + this.helper = $("
      "); + this._addClass( this.helper, "ui-selectable-helper" ); }, _destroy: function() { - this.selectees - .removeClass("ui-selectee") - .removeData("selectable-item"); - this.element - .removeClass("ui-selectable ui-selectable-disabled"); + this.selectees.removeData("selectable-item"); this._mouseDestroy(); }, @@ -126,9 +120,9 @@ return $.widget("ui.selectable", $.ui.mouse, { var selectee = $.data(this, "selectable-item"); selectee.startselected = true; if (!event.metaKey && !event.ctrlKey) { - selectee.$element.removeClass("ui-selected"); + that._removeClass( selectee.$element, "ui-selected" ); selectee.selected = false; - selectee.$element.addClass("ui-unselecting"); + that._addClass( selectee.$element, "ui-unselecting" ); selectee.unselecting = true; // selectable UNSELECTING callback that._trigger("unselecting", event, { @@ -142,9 +136,8 @@ return $.widget("ui.selectable", $.ui.mouse, { selectee = $.data(this, "selectable-item"); if (selectee) { doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected"); - selectee.$element - .removeClass(doSelect ? "ui-unselecting" : "ui-selected") - .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); + that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" ) + ._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" ); selectee.unselecting = !doSelect; selectee.selecting = doSelect; selectee.selected = doSelect; @@ -202,15 +195,15 @@ return $.widget("ui.selectable", $.ui.mouse, { if (hit) { // SELECT if (selectee.selected) { - selectee.$element.removeClass("ui-selected"); + that._removeClass( selectee.$element, "ui-selected" ); selectee.selected = false; } if (selectee.unselecting) { - selectee.$element.removeClass("ui-unselecting"); + that._removeClass( selectee.$element, "ui-unselecting" ); selectee.unselecting = false; } if (!selectee.selecting) { - selectee.$element.addClass("ui-selecting"); + that._addClass( selectee.$element, "ui-selecting" ); selectee.selecting = true; // selectable SELECTING callback that._trigger("selecting", event, { @@ -221,15 +214,15 @@ return $.widget("ui.selectable", $.ui.mouse, { // UNSELECT if (selectee.selecting) { if ((event.metaKey || event.ctrlKey) && selectee.startselected) { - selectee.$element.removeClass("ui-selecting"); + that._removeClass( selectee.$element, "ui-selecting" ); selectee.selecting = false; - selectee.$element.addClass("ui-selected"); + that._addClass( selectee.$element, "ui-selected" ); selectee.selected = true; } else { - selectee.$element.removeClass("ui-selecting"); + that._removeClass( selectee.$element, "ui-selecting" ); selectee.selecting = false; if (selectee.startselected) { - selectee.$element.addClass("ui-unselecting"); + that._addClass( selectee.$element, "ui-unselecting" ); selectee.unselecting = true; } // selectable UNSELECTING callback @@ -240,10 +233,10 @@ return $.widget("ui.selectable", $.ui.mouse, { } if (selectee.selected) { if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { - selectee.$element.removeClass("ui-selected"); + that._removeClass( selectee.$element, "ui-selected" ); selectee.selected = false; - selectee.$element.addClass("ui-unselecting"); + that._addClass( selectee.$element, "ui-unselecting" ); selectee.unselecting = true; // selectable UNSELECTING callback that._trigger("unselecting", event, { @@ -264,7 +257,7 @@ return $.widget("ui.selectable", $.ui.mouse, { $(".ui-unselecting", this.element[0]).each(function() { var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-unselecting"); + that._removeClass( selectee.$element, "ui-unselecting" ); selectee.unselecting = false; selectee.startselected = false; that._trigger("unselected", event, { @@ -273,7 +266,8 @@ return $.widget("ui.selectable", $.ui.mouse, { }); $(".ui-selecting", this.element[0]).each(function() { var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-selecting").addClass("ui-selected"); + that._removeClass( selectee.$element, "ui-selecting" ) + ._addClass( selectee.$element, "ui-selected" ); selectee.selecting = false; selectee.selected = true; selectee.startselected = true; diff --git a/ui/selectmenu.js b/ui/selectmenu.js index 9bd2d1016a7..0d1354c6d79 100644 --- a/ui/selectmenu.js +++ b/ui/selectmenu.js @@ -39,6 +39,10 @@ return $.widget( "ui.selectmenu", { defaultElement: "", widgetEventPrefix: "spin", options: { + classes: { + "ui-spinner": "ui-corner-all", + "ui-spinner-down": "ui-corner-br", + "ui-spinner-up": "ui-corner-tr" + }, culture: null, icons: { down: "ui-icon-triangle-1-s", @@ -211,20 +216,31 @@ return $.widget( "ui.spinner", { _draw: function() { var uiSpinner = this.uiSpinner = this.element - .addClass( "ui-spinner-input" ) .attr( "autocomplete", "off" ) .wrap( this._uiSpinnerHtml() ) .parent() // add buttons .append( this._buttonHtml() ); + this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" ); + this._addClass( "ui-spinner-input" ); + this.element.attr( "role", "spinbutton" ); // button bindings - this.buttons = uiSpinner.find( ".ui-spinner-button" ) + this.buttons = uiSpinner.children( "a" ) .attr( "tabIndex", -1 ) - .button() - .removeClass( "ui-corner-all" ); + .button(); + + // TODO: Right now button does not support classes this is already updated in button PR + this._removeClass( this.buttons, "ui-corner-all" ); + + this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" ); + this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" ); + this._addClass( this.buttons.first().find( ".ui-button-text span" ), null, + "ui-icon " + this.options.icons.up ); + this._addClass( this.buttons.last().find( ".ui-button-text span" ), null, + "ui-icon " + this.options.icons.down ); // IE 6 doesn't understand height: 50% for the buttons // unless the wrapper has an explicit height @@ -262,16 +278,16 @@ return $.widget( "ui.spinner", { }, _uiSpinnerHtml: function() { - return ""; + return ""; }, _buttonHtml: function() { return "" + - "" + - "" + + "" + + "" + "" + - "" + - "" + + "" + + "" + ""; }, @@ -379,8 +395,10 @@ return $.widget( "ui.spinner", { }, _setOption: function( key, value ) { + var prevValue, first, last; + if ( key === "culture" || key === "numberFormat" ) { - var prevValue = this._parse( this.element.val() ); + prevValue = this._parse( this.element.val() ); this.options[ key ] = value; this.element.val( this._format( prevValue ) ); return; @@ -392,18 +410,18 @@ return $.widget( "ui.spinner", { } } if ( key === "icons" ) { - this.buttons.first().find( ".ui-icon" ) - .removeClass( this.options.icons.up ) - .addClass( value.up ); - this.buttons.last().find( ".ui-icon" ) - .removeClass( this.options.icons.down ) - .addClass( value.down ); + first = this.buttons.first().find( ".ui-icon" ); + this._removeClass( first, null, this.options.icons.up ); + this._addClass( first, null, value.up ); + last = this.buttons.last().find( ".ui-icon" ); + this._removeClass( last, null, this.options.icons.down ); + this._addClass( last, null, value.down ); } this._super( key, value ); if ( key === "disabled" ) { - this.widget().toggleClass( "ui-state-disabled", !!value ); + this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value ); this.element.prop( "disabled", !!value ); this.buttons.button( value ? "disable" : "enable" ); } @@ -469,13 +487,13 @@ return $.widget( "ui.spinner", { _destroy: function() { this.element - .removeClass( "ui-spinner-input" ) .prop( "disabled", false ) .removeAttr( "autocomplete" ) .removeAttr( "role" ) .removeAttr( "aria-valuemin" ) .removeAttr( "aria-valuemax" ) .removeAttr( "aria-valuenow" ); + this.uiSpinner.replaceWith( this.element ); }, diff --git a/ui/tabs.js b/ui/tabs.js index 6d826b14af1..366ebbb51c8 100644 --- a/ui/tabs.js +++ b/ui/tabs.js @@ -37,6 +37,12 @@ return $.widget( "ui.tabs", { delay: 300, options: { active: null, + classes: { + "ui-tabs": "ui-corner-all", + "ui-tabs-nav": "ui-corner-all", + "ui-tab": "ui-corner-top", + "ui-tabs-panel": "ui-corner-bottom" + }, collapsible: false, event: "click", heightStyle: "content", @@ -77,9 +83,8 @@ return $.widget( "ui.tabs", { this.running = false; - this.element - .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ) - .toggleClass( "ui-tabs-collapsible", options.collapsible ); + this._addClass( "ui-tabs", "ui-widget ui-widget-content" ); + this._toggleClass( "ui-tabs-collapsible", null, options.collapsible ); this._processTabs(); options.active = this._initialActive(); @@ -286,7 +291,8 @@ return $.widget( "ui.tabs", { this._super( key, value); if ( key === "collapsible" ) { - this.element.toggleClass( "ui-tabs-collapsible", value ); + this._toggleClass( "ui-tabs-collapsible", null, value ); + // Setting collapsible: false while collapsed; open first panel if ( !value && this.options.active === false ) { this._activate( 0 ); @@ -362,12 +368,12 @@ return $.widget( "ui.tabs", { this.tabs.eq( 0 ).attr( "tabIndex", 0 ); } else { this.active - .addClass( "ui-tabs-active ui-state-active" ) .attr({ "aria-selected": "true", "aria-expanded": "true", tabIndex: 0 }); + this._addClass( this.active, "ui-tabs-active", "ui-state-active" ); this._getPanelForTab( this.active ) .show() .attr({ @@ -382,11 +388,12 @@ return $.widget( "ui.tabs", { prevAnchors = this.anchors, prevPanels = this.panels; - this.tablist = this._getList() - .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) - .attr( "role", "tablist" ) + this.tablist = this._getList().attr( "role", "tablist" ); + this._addClass( this.tablist, "ui-tabs-nav", + "ui-helper-reset ui-helper-clearfix ui-widget-header" ); - // Prevent users from focusing disabled tabs via click + // Prevent users from focusing disabled tabs via click + this.tablist .delegate( "> li", "mousedown" + this.eventNamespace, function( event ) { if ( $( this ).is( ".ui-state-disabled" ) ) { event.preventDefault(); @@ -406,20 +413,20 @@ return $.widget( "ui.tabs", { }); this.tabs = this.tablist.find( "> li:has(a[href])" ) - .addClass( "ui-state-default ui-corner-top" ) .attr({ role: "tab", tabIndex: -1 }); + this._addClass( this.tabs, "ui-tab", "ui-state-default" ); this.anchors = this.tabs.map(function() { return $( "a", this )[ 0 ]; }) - .addClass( "ui-tabs-anchor" ) .attr({ role: "presentation", tabIndex: -1 }); + this._addClass( this.anchors, "ui-tabs-anchor" ); this.panels = $(); @@ -461,9 +468,8 @@ return $.widget( "ui.tabs", { panel.attr( "aria-labelledby", anchorId ); }); - this.panels - .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) - .attr( "role", "tabpanel" ); + this.panels.attr( "role", "tabpanel" ); + this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" ); // Avoid memory leaks (#10056) if ( prevTabs ) { @@ -481,11 +487,12 @@ return $.widget( "ui.tabs", { _createPanel: function( id ) { return $( "
      " ) .attr( "id", id ) - .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) .data( "ui-tabs-destroy", true ); }, _setupDisabled: function( disabled ) { + var currentItem, li, i; + if ( $.isArray( disabled ) ) { if ( !disabled.length ) { disabled = false; @@ -495,15 +502,14 @@ return $.widget( "ui.tabs", { } // disable tabs - for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) { + for ( i = 0; ( li = this.tabs[ i ] ); i++ ) { + currentItem = $( li ); if ( disabled === true || $.inArray( i, disabled ) !== -1 ) { - $( li ) - .addClass( "ui-state-disabled" ) - .attr( "aria-disabled", "true" ); + currentItem.attr( "aria-disabled", "true" ); + this._addClass( currentItem, null, "ui-state-disabled" ); } else { - $( li ) - .removeClass( "ui-state-disabled" ) - .removeAttr( "aria-disabled" ); + currentItem.removeAttr( "aria-disabled" ); + this._removeClass( currentItem, null, "ui-state-disabled" ); } } @@ -629,7 +635,7 @@ return $.widget( "ui.tabs", { } function show() { - eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" ); + that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" ); if ( toShow.length && that.options.show ) { that._show( toShow, that.options.show, complete ); @@ -642,11 +648,13 @@ return $.widget( "ui.tabs", { // start out by hiding, then showing, then completing if ( toHide.length && this.options.hide ) { this._hide( toHide, this.options.hide, function() { - eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); + that._removeClass( eventData.oldTab.closest( "li" ), + "ui-tabs-active", "ui-state-active" ); show(); }); } else { - eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); + this._removeClass( eventData.oldTab.closest( "li" ), + "ui-tabs-active", "ui-state-active" ); toHide.hide(); show(); } @@ -716,27 +724,20 @@ return $.widget( "ui.tabs", { this.xhr.abort(); } - this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ); - this.tablist - .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) - .removeAttr( "role" ); + .removeAttr( "role" ) + .unbind( this.eventNamespace ); this.anchors - .removeClass( "ui-tabs-anchor" ) .removeAttr( "role" ) .removeAttr( "tabIndex" ) .removeUniqueId(); - this.tablist.unbind( this.eventNamespace ); - this.tabs.add( this.panels ).each(function() { if ( $.data( this, "ui-tabs-destroy" ) ) { $( this ).remove(); } else { $( this ) - .removeClass( "ui-state-default ui-state-active ui-state-disabled " + - "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" ) .removeAttr( "tabIndex" ) .removeAttr( "aria-live" ) .removeAttr( "aria-busy" ) @@ -827,7 +828,7 @@ return $.widget( "ui.tabs", { that.panels.stop( false, true ); } - tab.removeClass( "ui-tabs-loading" ); + that._removeClass( tab, "ui-tabs-loading" ); panel.removeAttr( "aria-busy" ); if ( jqXHR === that.xhr ) { @@ -846,7 +847,7 @@ return $.widget( "ui.tabs", { // jQuery <1.8 returns false if the request is canceled in beforeSend, // but as of 1.8, $.ajax() always returns a jqXHR object. if ( this.xhr && this.xhr.statusText !== "canceled" ) { - tab.addClass( "ui-tabs-loading" ); + this._addClass( tab, "ui-tabs-loading" ); panel.attr( "aria-busy", "true" ); this.xhr diff --git a/ui/tooltip.js b/ui/tooltip.js index 2c28cc073e0..5708015d92b 100644 --- a/ui/tooltip.js +++ b/ui/tooltip.js @@ -33,9 +33,12 @@ } }(function( $ ) { -return $.widget( "ui.tooltip", { +$.widget( "ui.tooltip", { version: "@VERSION", options: { + classes: { + "ui-tooltip": "ui-corner-all ui-widget-shadow" + }, content: function() { // support: IE<9, Opera in jQuery <1.7 // .text() can't accept undefined, so coerce to a string @@ -52,7 +55,6 @@ return $.widget( "ui.tooltip", { collision: "flipfit flip" }, show: true, - tooltipClass: null, track: false, // callbacks @@ -109,8 +111,8 @@ return $.widget( "ui.tooltip", { "aria-live": "assertive", "aria-relevant": "additions" }) - .addClass( "ui-helper-hidden-accessible" ) .appendTo( this.document[ 0 ].body ); + this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); }, _setOption: function( key, value ) { @@ -419,16 +421,12 @@ return $.widget( "ui.tooltip", { }, _tooltip: function( element ) { - var tooltip = $( "
      " ) - .attr( "role", "tooltip" ) - // TODO move to classes option - .addClass( "ui-tooltip ui-widget ui-widget-shadow ui-corner-all ui-widget-content " + - ( this.options.tooltipClass || "" ) ), + var tooltip = $( "
      " ).attr( "role", "tooltip" ), + content = $( "
      " ).appendTo( tooltip ), id = tooltip.uniqueId().attr( "id" ); - $( "
      " ) - .addClass( "ui-tooltip-content" ) - .appendTo( tooltip ); + this._addClass( content, "ui-tooltip-content" ); + this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" ); tooltip.appendTo( this.document[0].body ); @@ -476,4 +474,25 @@ return $.widget( "ui.tooltip", { } }); +// DEPRECATED +// TODO: Switch return back to widget declaration at top of file when this is removed +if ( $.uiBackCompat !== false ) { + + // Backcompat for tooltipClass option + $.widget( "ui.tooltip", $.ui.tooltip, { + options: { + tooltipClass: null + }, + _tooltip: function() { + var tooltipData = this._superApply( arguments ); + if ( this.options.tooltipClass ) { + tooltipData.tooltip.addClass( this.options.tooltipClass ); + } + return tooltipData; + } + }); +} + +return $.ui.tooltip; + })); diff --git a/ui/widget.js b/ui/widget.js index 33e0d156e4d..4999048f6f0 100644 --- a/ui/widget.js +++ b/ui/widget.js @@ -250,6 +250,7 @@ $.Widget.prototype = { widgetEventPrefix: "", defaultElement: "
      ", options: { + classes: {}, disabled: false, // callbacks @@ -264,6 +265,7 @@ $.Widget.prototype = { this.bindings = $(); this.hoverable = $(); this.focusable = $(); + this.classesElementLookup = {}; if ( element !== this ) { $.data( element, this.widgetFullName, this ); @@ -297,7 +299,13 @@ $.Widget.prototype = { _init: $.noop, destroy: function() { + var that = this; + this._destroy(); + $.each( this.classesElementLookup, function( key, value ) { + that._removeClass( value, key ); + }); + // we can probably remove the unbind calls in 2.0 // all event bindings should go through this._on() this.element @@ -305,15 +313,10 @@ $.Widget.prototype = { .removeData( this.widgetFullName ); this.widget() .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); + .removeAttr( "aria-disabled" ); // clean up events and states this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); }, _destroy: $.noop, @@ -370,21 +373,54 @@ $.Widget.prototype = { return this; }, _setOption: function( key, value ) { + if ( key === "classes" ) { + this._setOptionClasses( value ); + } + this.options[ key ] = value; if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled", !!value ); + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); // If the widget is becoming disabled, then nothing is interactive if ( value ) { - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); + this._removeClass( this.hoverable, null, "ui-state-hover" ); + this._removeClass( this.focusable, null, "ui-state-focus" ); } } return this; }, + _setOptionClasses: function( value ) { + var classKey, elements, currentElements; + + for ( classKey in value ) { + currentElements = this.classesElementLookup[ classKey ]; + if ( value[ classKey ] === this.options.classes[ classKey ] || + !currentElements || + !currentElements.length ) { + continue; + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $( currentElements.get() ); + this._removeClass( currentElements, classKey ); + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( this._classes({ + element: elements, + keys: classKey, + classes: value, + add: true + })); + } + }, enable: function() { return this._setOptions({ disabled: false }); @@ -393,6 +429,63 @@ $.Widget.prototype = { return this._setOptions({ disabled: true }); }, + _classes: function( options ) { + var full = [], + that = this; + + options = $.extend({ + element: this.element, + classes: this.options.classes || {} + }, options ); + + function processClassString( classes, checkOption ) { + var current, i; + for ( i = 0; i < classes.length; i++ ) { + current = that.classesElementLookup[ classes[ i ] ] || $(); + if ( options.add ) { + current = $( $.unique( current.get().concat( options.element.get() ) ) ); + } else { + current = $( current.not( options.element ).get() ); + } + that.classesElementLookup[ classes[ i ] ] = current; + full.push( classes[ i ] ); + if ( checkOption && options.classes[ classes[ i ] ] ) { + full.push( options.classes[ classes[ i ] ] ); + } + } + } + + if ( options.keys ) { + processClassString( options.keys.match( /\S+/g ) || [], true ); + } + if ( options.extra ) { + processClassString( options.extra.match( /\S+/g ) || [] ); + } + + return full.join( " " ); + }, + + _removeClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, false ); + }, + + _addClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, true ); + }, + + _toggleClass: function( element, keys, extra, add ) { + add = ( typeof add === "boolean" ) ? add : extra; + var shift = ( typeof element === "string" || element === null ), + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + }; + options.element.toggleClass( this._classes( options ), add ); + return this; + }, + _on: function( suppressDisabledCheck, element, handlers ) { var delegateElement, instance = this; @@ -469,10 +562,10 @@ $.Widget.prototype = { this.hoverable = this.hoverable.add( element ); this._on( element, { mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); + this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); }, mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); + this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); } }); }, @@ -481,10 +574,10 @@ $.Widget.prototype = { this.focusable = this.focusable.add( element ); this._on( element, { focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); + this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); }, focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); + this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); } }); },