aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorFelix Nagel <info@felixnagel.com>2012-05-24 18:34:32 +0200
committerFelix Nagel <info@felixnagel.com>2012-05-24 18:34:32 +0200
commitb30184d885329317b9dbd70835d2c4d154f98475 (patch)
tree445acc2481e8456211c3e893e7789fe4b5978791 /ui
parentec6d88fae464ffb432df6c8d2ed06ee9fa5b4dae (diff)
parenta1f604eb453208c80ec17c42c7bb4e3a1c624102 (diff)
downloadjquery-ui-b30184d885329317b9dbd70835d2c4d154f98475.tar.gz
jquery-ui-b30184d885329317b9dbd70835d2c4d154f98475.zip
Merge with master
Diffstat (limited to 'ui')
-rw-r--r--ui/i18n/jquery.ui.datepicker-eu.js18
-rw-r--r--ui/jquery.effects.core.js12
-rw-r--r--ui/jquery.ui.accordion.js14
-rw-r--r--ui/jquery.ui.autocomplete.js112
-rw-r--r--ui/jquery.ui.button.js11
-rw-r--r--ui/jquery.ui.core.js79
-rw-r--r--ui/jquery.ui.dialog.js24
-rw-r--r--ui/jquery.ui.menu.js85
-rw-r--r--ui/jquery.ui.mouse.js2
-rw-r--r--ui/jquery.ui.resizable.js6
-rw-r--r--ui/jquery.ui.slider.js5
-rw-r--r--ui/jquery.ui.sortable.js3
-rw-r--r--ui/jquery.ui.tabs.js267
-rw-r--r--ui/jquery.ui.tooltip.js38
14 files changed, 425 insertions, 251 deletions
diff --git a/ui/i18n/jquery.ui.datepicker-eu.js b/ui/i18n/jquery.ui.datepicker-eu.js
index 4c40eebec..846ce3abc 100644
--- a/ui/i18n/jquery.ui.datepicker-eu.js
+++ b/ui/i18n/jquery.ui.datepicker-eu.js
@@ -6,15 +6,15 @@ jQuery(function($){
prevText: '&#x3C;Aur',
nextText: 'Hur&#x3E;',
currentText: 'Gaur',
- monthNames: ['Urtarrila','Otsaila','Martxoa','Apirila','Maiatza','Ekaina',
- 'Uztaila','Abuztua','Iraila','Urria','Azaroa','Abendua'],
- monthNamesShort: ['Urt','Ots','Mar','Api','Mai','Eka',
- 'Uzt','Abu','Ira','Urr','Aza','Abe'],
- dayNames: ['Igandea','Astelehena','Asteartea','Asteazkena','Osteguna','Ostirala','Larunbata'],
- dayNamesShort: ['Iga','Ast','Ast','Ast','Ost','Ost','Lar'],
- dayNamesMin: ['Ig','As','As','As','Os','Os','La'],
- weekHeader: 'Wk',
- dateFormat: 'yy/mm/dd',
+ monthNames: ['urtarrila','otsaila','martxoa','apirila','maiatza','ekaina',
+ 'uztaila','abuztua','iraila','urria','azaroa','abendua'],
+ monthNamesShort: ['urt.','ots.','mar.','api.','mai.','eka.',
+ 'uzt.','abu.','ira.','urr.','aza.','abe.'],
+ dayNames: ['igandea','astelehena','asteartea','asteazkena','osteguna','ostirala','larunbata'],
+ dayNamesShort: ['ig.','al.','ar.','az.','og.','ol.','lr.'],
+ dayNamesMin: ['ig','al','ar','az','og','ol','lr'],
+ weekHeader: 'As',
+ dateFormat: 'yy-mm-dd',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
diff --git a/ui/jquery.effects.core.js b/ui/jquery.effects.core.js
index fe2bf298d..1bb1415a9 100644
--- a/ui/jquery.effects.core.js
+++ b/ui/jquery.effects.core.js
@@ -54,27 +54,27 @@ function getRGB(color) {
}
// Look for rgb(num,num,num)
- if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
+ if ( (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) ) {
return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
}
// Look for rgb(num%,num%,num%)
- if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) {
+ if ( (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) ) {
return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
}
// Look for #a0b1c2
- if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) {
+ if ( (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) ) {
return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
}
// Look for #fff
- if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) {
+ if ( (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) ) {
return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
}
// Look for rgba(0, 0, 0, 0) == transparent in Safari 3
- if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) {
+ if ( (result = /rgba\(0, 0, 0, 0\)/.exec(color)) ) {
return colors.transparent;
}
@@ -94,7 +94,7 @@ function getColor(elem, attr) {
}
attr = "backgroundColor";
- } while ( elem = elem.parentNode );
+ } while ( (elem = elem.parentNode) );
return getRGB(color);
}
diff --git a/ui/jquery.ui.accordion.js b/ui/jquery.ui.accordion.js
index fe1771bc8..f713d25b8 100644
--- a/ui/jquery.ui.accordion.js
+++ b/ui/jquery.ui.accordion.js
@@ -63,7 +63,8 @@ $.widget( "ui.accordion", {
this._focusable( this.headers );
this.headers.next()
- .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );
+ .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
+ .hide();
// don't allow collapsible: false and active: false
if ( !options.collapsible && options.active === false ) {
@@ -76,7 +77,9 @@ $.widget( "ui.accordion", {
this.active = this._findActive( options.active )
.addClass( "ui-accordion-header-active ui-state-active" )
.toggleClass( "ui-corner-all ui-corner-top" );
- this.active.next().addClass( "ui-accordion-content-active" );
+ this.active.next()
+ .addClass( "ui-accordion-content-active" )
+ .show();
this._createIcons();
this.originalHeight = this.element[0].style.height;
@@ -290,10 +293,9 @@ $.widget( "ui.accordion", {
},
refresh: function() {
- var heightStyle = this.options.heightStyle,
- parent = this.element.parent(),
- maxHeight,
- overflow;
+ var maxHeight, overflow,
+ heightStyle = this.options.heightStyle,
+ parent = this.element.parent();
this.element.css( "height", this.originalHeight );
diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js
index fab9691a3..e5cc9e693 100644
--- a/ui/jquery.ui.autocomplete.js
+++ b/ui/jquery.ui.autocomplete.js
@@ -57,16 +57,11 @@ $.widget( "ui.autocomplete", {
this.isMultiLine = this.element.is( "textarea,[contenteditable]" );
this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];
+ this.isNewMenu = true;
this.element
.addClass( "ui-autocomplete-input" )
- .attr( "autocomplete", "off" )
- // TODO verify these actually work as intended
- .attr({
- role: "textbox",
- "aria-autocomplete": "list",
- "aria-haspopup": "true"
- });
+ .attr( "autocomplete", "off" );
this._bind({
keydown: function( event ) {
@@ -115,7 +110,7 @@ $.widget( "ui.autocomplete", {
}
break;
case keyCode.ESCAPE:
- if ( this.menu.element.is(":visible") ) {
+ if ( this.menu.element.is( ":visible" ) ) {
this._value( this.term );
this.close( event );
// Different browsers have different default behavior for escape
@@ -183,12 +178,14 @@ $.widget( "ui.autocomplete", {
});
this._initSource();
- this.menu = $( "<ul></ul>" )
+ this.menu = $( "<ul>" )
.addClass( "ui-autocomplete" )
- .appendTo( this.document.find( this.options.appendTo || "body" )[0] )
+ .appendTo( this.document.find( this.options.appendTo || "body" )[ 0 ] )
.menu({
// custom key handling for now
- input: $()
+ input: $(),
+ // disable ARIA support, the live region takes care of that
+ role: null
})
.zIndex( this.element.zIndex() + 1 )
.hide()
@@ -213,7 +210,7 @@ $.widget( "ui.autocomplete", {
if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
this._delay(function() {
var that = this;
- this.document.one( 'mousedown', function( event ) {
+ this.document.one( "mousedown", function( event ) {
if ( event.target !== that.element[ 0 ] &&
event.target !== menuElement &&
!$.contains( menuElement, event.target ) ) {
@@ -224,14 +221,35 @@ $.widget( "ui.autocomplete", {
}
},
menufocus: function( event, ui ) {
+ // #7024 - Prevent accidental activation of menu items in Firefox
+ if ( this.isNewMenu ) {
+ this.isNewMenu = false;
+ if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
+ this.menu.blur();
+
+ this.document.one( "mousemove", function() {
+ $( event.target ).trigger( event.originalEvent );
+ });
+
+ return;
+ }
+ }
+
// back compat for _renderItem using item.autocomplete, via #7810
// TODO remove the fallback, see #8156
var item = ui.item.data( "ui-autocomplete-item" ) || ui.item.data( "item.autocomplete" );
if ( false !== this._trigger( "focus", event, { item: item } ) ) {
// use value to match what will end up in the input, if it was a key event
- if ( /^key/.test(event.originalEvent.type) ) {
+ if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
this._value( item.value );
}
+ } else {
+ // Normally the input is populated with the item's value as the
+ // menu is navigated, causing screen readers to notice a change and
+ // announce the item. Since the focus event was canceled, this doesn't
+ // happen, so we update the live region so that screen readers can
+ // still notice the change and announce it.
+ this.liveRegion.text( item.value );
}
},
menuselect: function( event, ui ) {
@@ -265,6 +283,13 @@ $.widget( "ui.autocomplete", {
}
});
+ this.liveRegion = $( "<span>", {
+ role: "status",
+ "aria-live": "polite"
+ })
+ .addClass( "ui-helper-hidden-accessible" )
+ .insertAfter( this.element );
+
if ( $.fn.bgiframe ) {
this.menu.element.bgiframe();
}
@@ -283,11 +308,9 @@ $.widget( "ui.autocomplete", {
clearTimeout( this.searching );
this.element
.removeClass( "ui-autocomplete-input" )
- .removeAttr( "autocomplete" )
- .removeAttr( "role" )
- .removeAttr( "aria-autocomplete" )
- .removeAttr( "aria-haspopup" );
+ .removeAttr( "autocomplete" );
this.menu.element.remove();
+ this.liveRegion.remove();
},
_setOption: function( key, value ) {
@@ -304,13 +327,12 @@ $.widget( "ui.autocomplete", {
},
_initSource: function() {
- var that = this,
- array,
- url;
+ var array, url,
+ that = this;
if ( $.isArray(this.options.source) ) {
array = this.options.source;
this.source = function( request, response ) {
- response( $.ui.autocomplete.filter(array, request.term) );
+ response( $.ui.autocomplete.filter( array, request.term ) );
};
} else if ( typeof this.options.source === "string" ) {
url = this.options.source;
@@ -408,9 +430,10 @@ $.widget( "ui.autocomplete", {
_close: function( event ) {
clearTimeout( this.closing );
- if ( this.menu.element.is(":visible") ) {
+ if ( this.menu.element.is( ":visible" ) ) {
this.menu.element.hide();
this.menu.blur();
+ this.isNewMenu = true;
this._trigger( "close", event );
}
},
@@ -426,7 +449,7 @@ $.widget( "ui.autocomplete", {
if ( items.length && items[0].label && items[0].value ) {
return items;
}
- return $.map( items, function(item) {
+ return $.map( items, function( item ) {
if ( typeof item === "string" ) {
return {
label: item,
@@ -457,7 +480,7 @@ $.widget( "ui.autocomplete", {
}, this.options.position ));
if ( this.options.autoFocus ) {
- this.menu.next( new $.Event("mouseover") );
+ this.menu.next();
}
},
@@ -483,18 +506,18 @@ $.widget( "ui.autocomplete", {
},
_renderItem: function( ul, item ) {
- return $( "<li></li>" )
- .append( $( "<a></a>" ).text( item.label ) )
+ return $( "<li>" )
+ .append( $( "<a>" ).text( item.label ) )
.appendTo( ul );
},
_move: function( direction, event ) {
- if ( !this.menu.element.is(":visible") ) {
+ if ( !this.menu.element.is( ":visible" ) ) {
this.search( null, event );
return;
}
- if ( this.menu.isFirstItem() && /^previous/.test(direction) ||
- this.menu.isLastItem() && /^next/.test(direction) ) {
+ if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
+ this.menu.isLastItem() && /^next/.test( direction ) ) {
this._value( this.term );
this.menu.blur();
return;
@@ -532,4 +555,35 @@ $.extend( $.ui.autocomplete, {
}
});
+
+// live region extension, adding a `messages` option
+// NOTE: This is an experimental API. We are still investigating
+// a full solution for string manipulation and internationalization.
+$.widget( "ui.autocomplete", $.ui.autocomplete, {
+ options: {
+ messages: {
+ noResults: "No search results.",
+ results: function(amount) {
+ return amount + ( amount > 1 ? " results are" : " result is" ) +
+ " available, use up and down arrow keys to navigate.";
+ }
+ }
+ },
+
+ __response: function( content ) {
+ var message;
+ this._superApply( arguments );
+ if ( this.options.disabled || this.cancelSearch) {
+ return;
+ }
+ if ( content && content.length ) {
+ message = this.options.messages.results( content.length );
+ } else {
+ message = this.options.messages.noResults;
+ }
+ this.liveRegion.text( message );
+ }
+});
+
+
}( jQuery ));
diff --git a/ui/jquery.ui.button.js b/ui/jquery.ui.button.js
index f84d748cf..810191775 100644
--- a/ui/jquery.ui.button.js
+++ b/ui/jquery.ui.button.js
@@ -73,7 +73,7 @@ $.widget( "ui.button", {
focusClass = "ui-state-focus";
if ( options.label === null ) {
- options.label = this.buttonElement.html();
+ options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
}
this.buttonElement
@@ -214,9 +214,9 @@ $.widget( "ui.button", {
_determineButtonType: function() {
var ancestor, labelSelector, checked;
- if ( this.element.is(":checkbox") ) {
+ if ( this.element.is("[type=checkbox]") ) {
this.type = "checkbox";
- } else if ( this.element.is(":radio") ) {
+ } else if ( this.element.is("[type=radio]") ) {
this.type = "radio";
} else if ( this.element.is("input") ) {
this.type = "input";
@@ -354,11 +354,10 @@ $.widget( "ui.button", {
}
});
-$.ui.button.version = "@VERSION";
-
$.widget( "ui.buttonset", {
+ version: "@VERSION",
options: {
- items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)"
+ items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(button)"
},
_create: function() {
diff --git a/ui/jquery.ui.core.js b/ui/jquery.ui.core.js
index 93353996e..d8fff91e1 100644
--- a/ui/jquery.ui.core.js
+++ b/ui/jquery.ui.core.js
@@ -119,49 +119,52 @@ $.fn.extend({
}
});
-$.each( [ "Width", "Height" ], function( i, name ) {
- var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
- type = name.toLowerCase(),
- orig = {
- innerWidth: $.fn.innerWidth,
- innerHeight: $.fn.innerHeight,
- outerWidth: $.fn.outerWidth,
- outerHeight: $.fn.outerHeight
- };
+// support: jQuery <1.8
+if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
+ $.each( [ "Width", "Height" ], function( i, name ) {
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
+ type = name.toLowerCase(),
+ orig = {
+ innerWidth: $.fn.innerWidth,
+ innerHeight: $.fn.innerHeight,
+ outerWidth: $.fn.outerWidth,
+ outerHeight: $.fn.outerHeight
+ };
+
+ function reduce( elem, size, border, margin ) {
+ $.each( side, function() {
+ size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
+ if ( border ) {
+ size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
+ }
+ if ( margin ) {
+ size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
+ }
+ });
+ return size;
+ }
- function reduce( elem, size, border, margin ) {
- $.each( side, function() {
- size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
- if ( border ) {
- size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
+ $.fn[ "inner" + name ] = function( size ) {
+ if ( size === undefined ) {
+ return orig[ "inner" + name ].call( this );
}
- if ( margin ) {
- size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
- }
- });
- return size;
- }
-
- $.fn[ "inner" + name ] = function( size ) {
- if ( size === undefined ) {
- return orig[ "inner" + name ].call( this );
- }
- return this.each(function() {
- $( this ).css( type, reduce( this, size ) + "px" );
- });
- };
+ return this.each(function() {
+ $( this ).css( type, reduce( this, size ) + "px" );
+ });
+ };
- $.fn[ "outer" + name] = function( size, margin ) {
- if ( typeof size !== "number" ) {
- return orig[ "outer" + name ].call( this, size );
- }
+ $.fn[ "outer" + name] = function( size, margin ) {
+ if ( typeof size !== "number" ) {
+ return orig[ "outer" + name ].call( this, size );
+ }
- return this.each(function() {
- $( this).css( type, reduce( this, size, true, margin ) + "px" );
- });
- };
-});
+ return this.each(function() {
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
+ });
+ };
+ });
+}
// selectors
function focusable( element, isTabIndexNotNaN ) {
diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js
index f60a1f78d..e48c26400 100644
--- a/ui/jquery.ui.dialog.js
+++ b/ui/jquery.ui.dialog.js
@@ -143,7 +143,14 @@ $.widget("ui.dialog", {
.addClass( "ui-dialog-title" )
.attr( "id", titleId )
.html( title )
- .prependTo( uiDialogTitlebar );
+ .prependTo( uiDialogTitlebar ),
+
+ uiDialogButtonPane = ( this.uiDialogButtonPane = $( "<div>" ) )
+ .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" ),
+
+ uiButtonSet = ( this.uiButtonSet = $( "<div>" ) )
+ .addClass( "ui-dialog-buttonset" )
+ .appendTo( uiDialogButtonPane );
uiDialogTitlebar.find( "*" ).add( uiDialogTitlebar ).disableSelection();
this._hoverable( uiDialogTitlebarClose );
@@ -326,7 +333,7 @@ $.widget("ui.dialog", {
// if there are no tabbable elements, set focus on the dialog itself
hasFocus = this.element.find( ":tabbable" );
if ( !hasFocus.length ) {
- hasFocus = uiDialog.find( ".ui-dialog-buttonpane :tabbable" );
+ hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
if ( !hasFocus.length ) {
hasFocus = uiDialog;
}
@@ -345,7 +352,8 @@ $.widget("ui.dialog", {
hasButtons = false;
// if we already have a button pane, remove it
- this.uiDialog.find( ".ui-dialog-buttonpane" ).remove();
+ this.uiDialogButtonPane.remove();
+ this.uiButtonSet.empty();
if ( typeof buttons === "object" && buttons !== null ) {
$.each( buttons, function() {
@@ -353,12 +361,6 @@ $.widget("ui.dialog", {
});
}
if ( hasButtons ) {
- uiDialogButtonPane = $( "<div>" )
- .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" );
- uiButtonSet = $( "<div>" )
- .addClass( "ui-dialog-buttonset" )
- .appendTo( uiDialogButtonPane );
-
$.each( buttons, function( name, props ) {
props = $.isFunction( props ) ?
{ click: props, text: name } :
@@ -369,13 +371,13 @@ $.widget("ui.dialog", {
.click(function() {
props.click.apply( that.element[0], arguments );
})
- .appendTo( uiButtonSet );
+ .appendTo( that.uiButtonSet );
if ( $.fn.button ) {
button.button();
}
});
this.uiDialog.addClass( "ui-dialog-buttons" );
- uiDialogButtonPane.appendTo( this.uiDialog );
+ this.uiDialogButtonPane.appendTo( this.uiDialog );
} else {
this.uiDialog.removeClass( "ui-dialog-buttons" );
}
diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js
index 99df2e629..1bfd76716 100644
--- a/ui/jquery.ui.menu.js
+++ b/ui/jquery.ui.menu.js
@@ -26,6 +26,7 @@ $.widget( "ui.menu", {
my: "left top",
at: "right top"
},
+ role: "menu",
// callbacks
blur: null,
@@ -42,7 +43,7 @@ $.widget( "ui.menu", {
.addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
.attr({
id: this.menuId,
- role: "menu",
+ role: this.options.role,
tabIndex: 0
})
// need to catch all clicks on disabled menu
@@ -201,13 +202,11 @@ $.widget( "ui.menu", {
event.preventDefault();
break;
case $.ui.keyCode.ENTER:
- if ( !this.active.is( ".ui-state-disabled" ) ) {
- if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
- this.expand( event );
- } else {
- this.select( event );
- }
- }
+ this._activate( event );
+ event.preventDefault();
+ break;
+ case $.ui.keyCode.SPACE:
+ this._activate( event );
event.preventDefault();
break;
case $.ui.keyCode.ESCAPE:
@@ -259,6 +258,16 @@ $.widget( "ui.menu", {
}
},
+ _activate: function( event ) {
+ if ( !this.active.is( ".ui-state-disabled" ) ) {
+ if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
+ this.expand( event );
+ } else {
+ this.select( event );
+ }
+ }
+ },
+
refresh: function() {
// initialize nested menus
var menus,
@@ -267,7 +276,7 @@ $.widget( "ui.menu", {
.addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
.hide()
.attr({
- role: "menu",
+ role: this.options.role,
"aria-hidden": "true",
"aria-expanded": "false"
});
@@ -281,7 +290,7 @@ $.widget( "ui.menu", {
.children( "a" )
.addClass( "ui-corner-all" )
.attr( "tabIndex", -1 )
- .attr( "role", "menuitem" )
+ .attr( "role", this._itemRole() )
.attr( "id", function( i ) {
return menuId + "-" + i;
});
@@ -302,30 +311,26 @@ $.widget( "ui.menu", {
});
},
+ _itemRole: function() {
+ return {
+ menu: "menuitem",
+ listbox: "option"
+ }[ this.options.role ];
+ },
+
focus: function( event, item ) {
- var nested, borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
+ var nested, focused;
this.blur( event, event && event.type === "focus" );
- if ( this._hasScroll() ) {
- borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
- paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
- offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
- scroll = this.activeMenu.scrollTop();
- elementHeight = this.activeMenu.height();
- itemHeight = item.height();
-
- if ( offset < 0 ) {
- this.activeMenu.scrollTop( scroll + offset );
- } else if ( offset + itemHeight > elementHeight ) {
- this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
- }
- }
+ this._scrollIntoView( item );
this.active = item.first();
- this.element.attr( "aria-activedescendant",
- this.active.children( "a" )
- .addClass( "ui-state-focus" )
- .attr( "id" ) );
+ focused = this.active.children( "a" ).addClass( "ui-state-focus" );
+ // only update aria-activedescendant if there's a role
+ // otherwise we assume focus is managed elsewhere
+ if ( this.options.role ) {
+ this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
+ }
// highlight active parent menu item, if any
this.active.parent().closest( ".ui-menu-item" ).children( "a:first" ).addClass( "ui-state-active" );
@@ -347,6 +352,24 @@ $.widget( "ui.menu", {
this._trigger( "focus", event, { item: item } );
},
+ _scrollIntoView: function( item ) {
+ var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
+ if ( this._hasScroll() ) {
+ borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
+ paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
+ offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
+ scroll = this.activeMenu.scrollTop();
+ elementHeight = this.activeMenu.height();
+ itemHeight = item.height();
+
+ if ( offset < 0 ) {
+ this.activeMenu.scrollTop( scroll + offset );
+ } else if ( offset + itemHeight > elementHeight ) {
+ this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
+ }
+ }
+ },
+
blur: function( event, fromFocus ) {
if ( !fromFocus ) {
clearTimeout( this.timer );
@@ -385,8 +408,8 @@ $.widget( "ui.menu", {
var position = $.extend( {}, {
of: this.active
- }, $.type(this.options.position) === "function" ?
- this.options.position(this.active) :
+ }, $.type( this.options.position ) === "function" ?
+ this.options.position( this.active ) :
this.options.position
);
diff --git a/ui/jquery.ui.mouse.js b/ui/jquery.ui.mouse.js
index eaa953ad0..e2e06897c 100644
--- a/ui/jquery.ui.mouse.js
+++ b/ui/jquery.ui.mouse.js
@@ -20,7 +20,7 @@ $( document ).mouseup( function( e ) {
$.widget("ui.mouse", {
version: "@VERSION",
options: {
- cancel: ':input,option',
+ cancel: 'input,textarea,button,select,option',
distance: 1,
delay: 0
},
diff --git a/ui/jquery.ui.resizable.js b/ui/jquery.ui.resizable.js
index b4433b618..6039cf648 100644
--- a/ui/jquery.ui.resizable.js
+++ b/ui/jquery.ui.resizable.js
@@ -171,12 +171,12 @@ $.widget("ui.resizable", $.ui.mouse, {
this._handles.hide();
$(this.element)
.addClass("ui-resizable-autohide")
- .hover(function() {
+ .mouseenter(function() {
if (o.disabled) return;
$(this).removeClass("ui-resizable-autohide");
that._handles.show();
- },
- function(){
+ })
+ .mouseleave(function(){
if (o.disabled) return;
if (!that.resizing) {
$(this).addClass("ui-resizable-autohide");
diff --git a/ui/jquery.ui.slider.js b/ui/jquery.ui.slider.js
index accd3e189..c302bfe8b 100644
--- a/ui/jquery.ui.slider.js
+++ b/ui/jquery.ui.slider.js
@@ -90,11 +90,12 @@ $.widget( "ui.slider", $.ui.mouse, {
.click(function( event ) {
event.preventDefault();
})
- .hover(function() {
+ .mouseenter(function() {
if ( !o.disabled ) {
$( this ).addClass( "ui-state-hover" );
}
- }, function() {
+ })
+ .mouseleave(function() {
$( this ).removeClass( "ui-state-hover" );
})
.focus(function() {
diff --git a/ui/jquery.ui.sortable.js b/ui/jquery.ui.sortable.js
index 55a64590b..88c8aa374 100644
--- a/ui/jquery.ui.sortable.js
+++ b/ui/jquery.ui.sortable.js
@@ -731,9 +731,10 @@ $.widget("ui.sortable", $.ui.mouse, {
var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
for (var j = this.items.length - 1; j >= 0; j--) {
if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
- var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top'];
+ var cur = this.containers[innermostIndex].floating ? this.items[j].item.offset().left : this.items[j].item.offset().top;
if(Math.abs(cur - base) < dist) {
dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
+ this.direction = (cur - base > 0) ? 'down' : 'up';
}
}
diff --git a/ui/jquery.ui.tabs.js b/ui/jquery.ui.tabs.js
index 026c50993..a693899da 100644
--- a/ui/jquery.ui.tabs.js
+++ b/ui/jquery.ui.tabs.js
@@ -25,7 +25,7 @@ function isLocal( anchor ) {
// if it's manually set, i.e., a.href = "#foo" kills the normalization
anchor = anchor.cloneNode( false );
return anchor.hash.length > 1 &&
- anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" );
+ anchor.href.replace( rhash, "" ) === location.href.replace( rhash, "" );
}
$.widget( "ui.tabs", {
@@ -34,7 +34,8 @@ $.widget( "ui.tabs", {
active: null,
collapsible: false,
event: "click",
- fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
+ hide: null,
+ show: null,
// callbacks
activate: null,
@@ -46,20 +47,20 @@ $.widget( "ui.tabs", {
_create: function() {
var panel,
that = this,
- options = that.options,
+ options = this.options,
active = options.active;
- that.running = false;
+ this.running = false;
- that.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
+ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
- that._processTabs();
+ this._processTabs();
if ( active === null ) {
// check the fragment identifier in the URL
if ( location.hash ) {
- that.anchors.each(function( i, tab ) {
- if ( tab.hash === location.hash ) {
+ this.anchors.each(function( i, anchor ) {
+ if ( anchor.hash === location.hash ) {
active = i;
return false;
}
@@ -68,12 +69,12 @@ $.widget( "ui.tabs", {
// check for a tab marked active via a class
if ( active === null ) {
- active = that.lis.filter( ".ui-tabs-active" ).index();
+ active = this.lis.filter( ".ui-tabs-active" ).index();
}
// no active tab, set to false
if ( active === null || active === -1 ) {
- active = that.lis.length ? 0 : false;
+ active = this.lis.length ? 0 : false;
}
}
@@ -101,8 +102,6 @@ $.widget( "ui.tabs", {
) ).sort();
}
- this._setupFx( options.fx );
-
this._refresh();
// highlight selected tab
@@ -111,7 +110,7 @@ $.widget( "ui.tabs", {
// check for length avoids error when initializing empty list
if ( options.active !== false && this.anchors.length ) {
this.active = this._findActive( options.active );
- panel = that._getPanelForTab( this.active );
+ panel = this._getPanelForTab( this.active );
panel.show();
this.lis.eq( options.active ).addClass( "ui-tabs-active ui-state-active" );
@@ -151,14 +150,10 @@ $.widget( "ui.tabs", {
if ( key === "event" ) {
this._setupEvents( value );
}
-
- if ( key === "fx" ) {
- this._setupFx( value );
- }
},
- _tabId: function( a ) {
- return $( a ).attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
+ _tabId: function( tab ) {
+ return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
},
_sanitizeSelector: function( hash ) {
@@ -192,7 +187,7 @@ $.widget( "ui.tabs", {
// was active, active tab still exists
} else {
// make sure active index is correct
- options.active = this.anchors.index( this.active );
+ options.active = this.lis.index( this.active );
}
},
@@ -202,6 +197,7 @@ $.widget( "ui.tabs", {
this.element.toggleClass( "ui-tabs-collapsible", options.collapsible );
this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
this.lis.addClass( "ui-state-default ui-corner-top" );
+ this.anchors.addClass( "ui-tabs-anchor" );
this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
this._setupDisabled( options.disabled );
@@ -224,7 +220,8 @@ $.widget( "ui.tabs", {
this.panels = $();
this.anchors.each(function( i, a ) {
- var selector, panel, id;
+ var selector, panel, id,
+ tab = $( a ).closest( "li" );
// inline tab
if ( isLocal( a ) ) {
@@ -232,7 +229,7 @@ $.widget( "ui.tabs", {
panel = that.element.find( that._sanitizeSelector( selector ) );
// remote tab
} else {
- id = that._tabId( a );
+ id = that._tabId( tab );
selector = "#" + id;
panel = that.element.find( selector );
if ( !panel.length ) {
@@ -244,7 +241,7 @@ $.widget( "ui.tabs", {
if ( panel.length) {
that.panels = that.panels.add( panel );
}
- $( a ).attr( "aria-controls", selector.substring( 1 ) );
+ tab.attr( "aria-controls", selector.substring( 1 ) );
});
},
@@ -278,49 +275,34 @@ $.widget( "ui.tabs", {
this.options.disabled = disabled;
},
- _setupFx: function( fx ) {
- // set up animations
- if ( fx ) {
- if ( $.isArray( fx ) ) {
- this.hideFx = fx[ 0 ];
- this.showFx = fx[ 1 ];
- } else {
- this.hideFx = this.showFx = fx;
- }
- }
- },
-
_setupEvents: function( event ) {
- // attach tab event handler, unbind to avoid duplicates from former tabifying...
- this.anchors.unbind( ".tabs" );
-
- // TODO: use event delegation via _bind()
+ var events = {
+ click: function( event ) {
+ event.preventDefault();
+ }
+ };
if ( event ) {
- this.anchors.bind( event.split( " " ).join( ".tabs " ) + ".tabs",
- $.proxy( this, "_eventHandler" ) );
+ $.each( event.split(" "), function( index, eventName ) {
+ events[ eventName ] = "_eventHandler";
+ });
}
-
- // TODO: use event delegation via _bind()
- // disable click in any case
- this.anchors.bind( "click.tabs", function( event ){
- event.preventDefault();
- });
+ this.anchors.unbind( ".tabs" );
+ this._bind( this.anchors, events );
},
_eventHandler: function( event ) {
- var that = this,
- options = that.options,
- active = that.active,
- clicked = $( event.currentTarget ),
- clickedIsActive = clicked[ 0 ] === active[ 0 ],
+ var options = this.options,
+ active = this.active,
+ anchor = $( event.currentTarget ),
+ tab = anchor.closest( "li" ),
+ clickedIsActive = tab[ 0 ] === active[ 0 ],
collapsing = clickedIsActive && options.collapsible,
- toShow = collapsing ? $() : that._getPanelForTab( clicked ),
- toHide = !active.length ? $() : that._getPanelForTab( active ),
- tab = clicked.closest( "li" ),
+ toShow = collapsing ? $() : this._getPanelForTab( tab ),
+ toHide = !active.length ? $() : this._getPanelForTab( active ),
eventData = {
oldTab: active,
oldPanel: toHide,
- newTab: collapsing ? $() : clicked,
+ newTab: collapsing ? $() : tab,
newPanel: toShow
};
@@ -330,32 +312,30 @@ $.widget( "ui.tabs", {
// tab is already loading
tab.hasClass( "ui-tabs-loading" ) ||
// can't switch durning an animation
- that.running ||
+ this.running ||
// click on active header, but not collapsible
( clickedIsActive && !options.collapsible ) ||
// allow canceling activation
- ( that._trigger( "beforeActivate", event, eventData ) === false ) ) {
- clicked[ 0 ].blur();
+ ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
return;
}
- options.active = collapsing ? false : that.anchors.index( clicked );
+ options.active = collapsing ? false : this.lis.index( tab );
- that.active = clickedIsActive ? $() : clicked;
- if ( that.xhr ) {
- that.xhr.abort();
+ this.active = clickedIsActive ? $() : tab;
+ if ( this.xhr ) {
+ this.xhr.abort();
}
if ( !toHide.length && !toShow.length ) {
- jQuery.error( "jQuery UI Tabs: Mismatching fragment identifier." );
+ $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
}
if ( toShow.length ) {
// TODO make passing in node possible
- that.load( that.anchors.index( clicked ), event );
- clicked[ 0 ].blur();
+ this.load( this.lis.index( tab ), event );
}
- that._toggle( event, eventData );
+ this._toggle( event, eventData );
},
// handles show/hide for selecting tabs
@@ -364,7 +344,7 @@ $.widget( "ui.tabs", {
toShow = eventData.newPanel,
toHide = eventData.oldPanel;
- that.running = true;
+ this.running = true;
function complete() {
that.running = false;
@@ -374,11 +354,8 @@ $.widget( "ui.tabs", {
function show() {
eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
- if ( toShow.length && that.showFx ) {
- toShow
- .animate( that.showFx, that.showFx.duration || "normal", function() {
- complete();
- });
+ if ( toShow.length && that.options.show ) {
+ that._show( toShow, that.options.show, complete );
} else {
toShow.show();
complete();
@@ -386,8 +363,8 @@ $.widget( "ui.tabs", {
}
// start out by hiding, then showing, then completing
- if ( toHide.length && that.hideFx ) {
- toHide.animate( that.hideFx, that.hideFx.duration || "normal", function() {
+ if ( toHide.length && this.options.hide ) {
+ this._hide( toHide, this.options.hide, function() {
eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
show();
});
@@ -399,31 +376,39 @@ $.widget( "ui.tabs", {
},
_activate: function( index ) {
- var active = this._findActive( index )[ 0 ];
+ var anchor,
+ active = this._findActive( index );
// trying to activate the already active panel
- if ( active === this.active[ 0 ] ) {
+ if ( active[ 0 ] === this.active[ 0 ] ) {
return;
}
// trying to collapse, simulate a click on the current active header
- active = active || this.active[ 0 ];
+ if ( !active.length ) {
+ active = this.active;
+ }
+ anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
this._eventHandler({
- target: active,
- currentTarget: active,
+ target: anchor,
+ currentTarget: anchor,
preventDefault: $.noop
});
},
_findActive: function( selector ) {
- return typeof selector === "number" ? this.anchors.eq( selector ) :
- typeof selector === "string" ? this.anchors.filter( "[href$='" + selector + "']" ) : $();
+ if ( typeof selector === "number" ) {
+ return this.lis.eq( selector );
+ }
+ if ( typeof selector === "string" ) {
+ return this.anchors.filter( "[href$='" + selector + "']" ).closest( "li" );
+ }
+ return $();
},
_getIndex: function( index ) {
// meta-function to give users option to provide a href string instead of a numerical index.
- // also sanitizes numerical indexes to valid values.
if ( typeof index === "string" ) {
index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
}
@@ -441,6 +426,7 @@ $.widget( "ui.tabs", {
this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
this.anchors
+ .removeClass( "ui-tabs-anchor" )
.unbind( ".tabs" )
.removeData( "href.tabs" )
.removeData( "load.tabs" );
@@ -513,10 +499,11 @@ $.widget( "ui.tabs", {
load: function( index, event ) {
index = this._getIndex( index );
var that = this,
- anchor = this.anchors.eq( index ),
- panel = that._getPanelForTab( anchor ),
+ tab = this.lis.eq( index ),
+ anchor = tab.find( ".ui-tabs-anchor" ),
+ panel = this._getPanelForTab( tab ),
eventData = {
- tab: anchor,
+ tab: tab,
panel: panel
};
@@ -533,27 +520,30 @@ $.widget( "ui.tabs", {
}
});
- if ( this.xhr ) {
- this.lis.eq( index ).addClass( "ui-tabs-loading" );
+ // support: jQuery <1.8
+ // 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.xhr
.success(function( response ) {
- // TODO: IE resolves cached XHRs immediately
- // remove when core #10467 is fixed
+ // support: jQuery <1.8
+ // http://bugs.jquery.com/ticket/11778
setTimeout(function() {
panel.html( response );
that._trigger( "load", event, eventData );
}, 1 );
})
.complete(function( jqXHR, status ) {
- // TODO: IE resolves cached XHRs immediately
- // remove when core #10467 is fixed
+ // support: jQuery <1.8
+ // http://bugs.jquery.com/ticket/11778
setTimeout(function() {
if ( status === "abort" ) {
that.panels.stop( false, true );
}
- that.lis.eq( index ).removeClass( "ui-tabs-loading" );
+ tab.removeClass( "ui-tabs-loading" );
if ( jqXHR === that.xhr ) {
delete that.xhr;
@@ -740,10 +730,10 @@ if ( $.uiBackCompat !== false ) {
.replace( /#\{label\}/g, label ) ),
id = !url.indexOf( "#" ) ?
url.replace( "#", "" ) :
- this._tabId( li.find( "a" )[ 0 ] );
+ this._tabId( li );
li.addClass( "ui-state-default ui-corner-top" ).data( "ui-tabs-destroy", true );
- li.find( "a" ).attr( "aria-controls", id );
+ li.attr( "aria-controls", id );
doInsertAfter = index >= this.lis.length;
@@ -786,7 +776,7 @@ if ( $.uiBackCompat !== false ) {
index = this._getIndex( index );
var options = this.options,
tab = this.lis.eq( index ).remove(),
- panel = this._getPanelForTab( tab.find( "a[aria-controls]" ) ).remove();
+ panel = this._getPanelForTab( tab ).remove();
// If selected tab was removed focus tab to the right or
// in case the last tab was removed the tab to the left.
@@ -825,8 +815,10 @@ if ( $.uiBackCompat !== false ) {
idPrefix: "ui-tabs-"
},
- _tabId: function( a ) {
- return $( a ).attr( "aria-controls" ) ||
+ _tabId: function( tab ) {
+ var a = tab.is( "li" ) ? tab.find( "a[href]" ) : tab;
+ a = a[0];
+ return $( a ).closest( "li" ).attr( "aria-controls" ) ||
a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF\-]/g, "" ) ||
this.options.idPrefix + getNextTabId();
}
@@ -892,7 +884,8 @@ if ( $.uiBackCompat !== false ) {
this._super();
if ( this.options.active !== false ) {
this._trigger( "show", null, this._ui(
- this.active[ 0 ], this._getPanelForTab( this.active )[ 0 ] ) );
+ this.active.find( ".ui-tabs-anchor" )[ 0 ],
+ this._getPanelForTab( this.active )[ 0 ] ) );
}
},
_trigger: function( type, event, data ) {
@@ -902,13 +895,13 @@ if ( $.uiBackCompat !== false ) {
}
if ( type === "beforeActivate" && data.newTab.length ) {
ret = this._super( "select", event, {
- tab: data.newTab[ 0],
+ tab: data.newTab.find( ".ui-tabs-anchor" )[ 0],
panel: data.newPanel[ 0 ],
index: data.newTab.closest( "li" ).index()
});
} else if ( type === "activate" && data.newTab.length ) {
ret = this._super( "show", event, {
- tab: data.newTab[ 0 ],
+ tab: data.newTab.find( ".ui-tabs-anchor" )[ 0 ],
panel: data.newPanel[ 0 ],
index: data.newTab.closest( "li" ).index()
});
@@ -990,11 +983,81 @@ if ( $.uiBackCompat !== false ) {
var _data = $.extend( {}, data );
if ( type === "load" ) {
_data.panel = _data.panel[ 0 ];
- _data.tab = _data.tab[ 0 ];
+ _data.tab = _data.tab.find( ".ui-tabs-anchor" )[ 0 ];
}
return this._super( type, event, _data );
}
});
+
+ // fx option
+ // The new animation options (show, hide) conflict with the old show callback.
+ // The old fx option wins over show/hide anyway (always favor back-compat).
+ // If a user wants to use the new animation API, they must give up the old API.
+ $.widget( "ui.tabs", $.ui.tabs, {
+ options: {
+ fx: null // e.g. { height: "toggle", opacity: "toggle", duration: 200 }
+ },
+
+ _getFx: function() {
+ var hide, show,
+ fx = this.options.fx;
+
+ if ( fx ) {
+ if ( $.isArray( fx ) ) {
+ hide = fx[ 0 ];
+ show = fx[ 1 ];
+ } else {
+ hide = show = fx;
+ }
+ }
+
+ return fx ? { show: show, hide: hide } : null;
+ },
+
+ _toggle: function( event, eventData ) {
+ var that = this,
+ toShow = eventData.newPanel,
+ toHide = eventData.oldPanel,
+ fx = this._getFx();
+
+ if ( !fx ) {
+ return this._super( event, eventData );
+ }
+
+ that.running = true;
+
+ function complete() {
+ that.running = false;
+ that._trigger( "activate", event, eventData );
+ }
+
+ function show() {
+ eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
+
+ if ( toShow.length && fx.show ) {
+ toShow
+ .animate( fx.show, fx.show.duration, function() {
+ complete();
+ });
+ } else {
+ toShow.show();
+ complete();
+ }
+ }
+
+ // start out by hiding, then showing, then completing
+ if ( toHide.length && fx.hide ) {
+ toHide.animate( fx.hide, fx.hide.duration, function() {
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
+ show();
+ });
+ } else {
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
+ toHide.hide();
+ show();
+ }
+ }
+ });
}
})( jQuery );
diff --git a/ui/jquery.ui.tooltip.js b/ui/jquery.ui.tooltip.js
index 47a377bfd..1892d6555 100644
--- a/ui/jquery.ui.tooltip.js
+++ b/ui/jquery.ui.tooltip.js
@@ -14,6 +14,31 @@
var increments = 0;
+function addDescribedBy( elem, id ) {
+ var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
+ describedby.push( id );
+ elem
+ .data( "ui-tooltip-id", id )
+ .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
+}
+
+function removeDescribedBy( elem ) {
+ var id = elem.data( "ui-tooltip-id" ),
+ describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
+ index = $.inArray( id, describedby );
+ if ( index !== -1 ) {
+ describedby.splice( index, 1 );
+ }
+
+ elem.removeData( "ui-tooltip-id" );
+ describedby = $.trim( describedby.join( " " ) );
+ if ( describedby ) {
+ elem.attr( "aria-describedby", describedby );
+ } else {
+ elem.removeAttr( "aria-describedby" );
+ }
+}
+
$.widget( "ui.tooltip", {
version: "@VERSION",
options: {
@@ -92,8 +117,8 @@ $.widget( "ui.tooltip", {
target = $( event ? event.target : this.element )
.closest( this.options.items );
- // if aria-describedby exists, then the tooltip is already open
- if ( !target.length || target.attr( "aria-describedby" ) ) {
+ // if ui-tooltip-id exists, then the tooltip is already open
+ if ( !target.length || target.data( "ui-tooltip-id" ) ) {
return;
}
@@ -127,7 +152,7 @@ $.widget( "ui.tooltip", {
// if we have a title, clear it to prevent the native tooltip
// we have to check first to avoid defining a title if none exists
// (we don't want to cause an element to start matching [title])
-
+ //
// We use removeAttr only for key events, to allow IE to export the correct
// accessible attributes. For mouse events, set to empty string to avoid
// native tooltip showing up (happens only when removing inside mouseover).
@@ -143,7 +168,7 @@ $.widget( "ui.tooltip", {
var tooltip = this._find( target );
if ( !tooltip.length ) {
tooltip = this._tooltip( target );
- target.attr( "aria-describedby", tooltip.attr( "id" ) );
+ addDescribedBy( target, tooltip.attr( "id" ) );
}
tooltip.find( ".ui-tooltip-content" ).html( content );
tooltip
@@ -183,6 +208,7 @@ $.widget( "ui.tooltip", {
// don't close if the element has focus
// this prevents the tooltip from closing if you hover while focused
+ //
// we have to check the event type because tabbing out of the document
// may leave the element as the activeElement
if ( !force && event && event.type !== "focusout" &&
@@ -195,7 +221,7 @@ $.widget( "ui.tooltip", {
target.attr( "title", target.data( "ui-tooltip-title" ) );
}
- target.removeAttr( "aria-describedby" );
+ removeDescribedBy( target );
tooltip.stop( true );
this._hide( tooltip, this.options.hide, function() {
@@ -232,7 +258,7 @@ $.widget( "ui.tooltip", {
},
_find: function( target ) {
- var id = target.attr( "aria-describedby" );
+ var id = target.data( "ui-tooltip-id" );
return id ? $( "#" + id ) : $();
},