aboutsummaryrefslogtreecommitdiffstats
path: root/ui/jquery.ui.autocomplete.js
diff options
context:
space:
mode:
Diffstat (limited to 'ui/jquery.ui.autocomplete.js')
-rw-r--r--ui/jquery.ui.autocomplete.js112
1 files changed, 83 insertions, 29 deletions
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 ));