1 | /*!
|
---|
2 | * jQuery UI Autocomplete 1.10.3
|
---|
3 | * http://jqueryui.com
|
---|
4 | *
|
---|
5 | * Copyright 2013 jQuery Foundation and other contributors
|
---|
6 | * Released under the MIT license.
|
---|
7 | * http://jquery.org/license
|
---|
8 | *
|
---|
9 | * http://api.jqueryui.com/autocomplete/
|
---|
10 | *
|
---|
11 | * Depends:
|
---|
12 | * jquery.ui.core.js
|
---|
13 | * jquery.ui.widget.js
|
---|
14 | * jquery.ui.position.js
|
---|
15 | * jquery.ui.menu.js
|
---|
16 | */
|
---|
17 | (function( $, undefined ) {
|
---|
18 |
|
---|
19 | // used to prevent race conditions with remote data sources
|
---|
20 | var requestIndex = 0;
|
---|
21 |
|
---|
22 | $.widget( "ui.autocomplete", {
|
---|
23 | version: "1.10.3",
|
---|
24 | defaultElement: "<input>",
|
---|
25 | options: {
|
---|
26 | appendTo: null,
|
---|
27 | autoFocus: false,
|
---|
28 | delay: 300,
|
---|
29 | minLength: 1,
|
---|
30 | position: {
|
---|
31 | my: "left top",
|
---|
32 | at: "left bottom",
|
---|
33 | collision: "none"
|
---|
34 | },
|
---|
35 | source: null,
|
---|
36 |
|
---|
37 | // callbacks
|
---|
38 | change: null,
|
---|
39 | close: null,
|
---|
40 | focus: null,
|
---|
41 | open: null,
|
---|
42 | response: null,
|
---|
43 | search: null,
|
---|
44 | select: null
|
---|
45 | },
|
---|
46 |
|
---|
47 | pending: 0,
|
---|
48 |
|
---|
49 | _create: function() {
|
---|
50 | // Some browsers only repeat keydown events, not keypress events,
|
---|
51 | // so we use the suppressKeyPress flag to determine if we've already
|
---|
52 | // handled the keydown event. #7269
|
---|
53 | // Unfortunately the code for & in keypress is the same as the up arrow,
|
---|
54 | // so we use the suppressKeyPressRepeat flag to avoid handling keypress
|
---|
55 | // events when we know the keydown event was used to modify the
|
---|
56 | // search term. #7799
|
---|
57 | var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
|
---|
58 | nodeName = this.element[0].nodeName.toLowerCase(),
|
---|
59 | isTextarea = nodeName === "textarea",
|
---|
60 | isInput = nodeName === "input";
|
---|
61 |
|
---|
62 | this.isMultiLine =
|
---|
63 | // Textareas are always multi-line
|
---|
64 | isTextarea ? true :
|
---|
65 | // Inputs are always single-line, even if inside a contentEditable element
|
---|
66 | // IE also treats inputs as contentEditable
|
---|
67 | isInput ? false :
|
---|
68 | // All other element types are determined by whether or not they're contentEditable
|
---|
69 | this.element.prop( "isContentEditable" );
|
---|
70 |
|
---|
71 | this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
|
---|
72 | this.isNewMenu = true;
|
---|
73 |
|
---|
74 | this.element
|
---|
75 | .addClass( "ui-autocomplete-input" )
|
---|
76 | .attr( "autocomplete", "off" );
|
---|
77 |
|
---|
78 | this._on( this.element, {
|
---|
79 | keydown: function( event ) {
|
---|
80 | /*jshint maxcomplexity:15*/
|
---|
81 | if ( this.element.prop( "readOnly" ) ) {
|
---|
82 | suppressKeyPress = true;
|
---|
83 | suppressInput = true;
|
---|
84 | suppressKeyPressRepeat = true;
|
---|
85 | return;
|
---|
86 | }
|
---|
87 |
|
---|
88 | suppressKeyPress = false;
|
---|
89 | suppressInput = false;
|
---|
90 | suppressKeyPressRepeat = false;
|
---|
91 | var keyCode = $.ui.keyCode;
|
---|
92 | switch( event.keyCode ) {
|
---|
93 | case keyCode.PAGE_UP:
|
---|
94 | suppressKeyPress = true;
|
---|
95 | this._move( "previousPage", event );
|
---|
96 | break;
|
---|
97 | case keyCode.PAGE_DOWN:
|
---|
98 | suppressKeyPress = true;
|
---|
99 | this._move( "nextPage", event );
|
---|
100 | break;
|
---|
101 | case keyCode.UP:
|
---|
102 | suppressKeyPress = true;
|
---|
103 | this._keyEvent( "previous", event );
|
---|
104 | break;
|
---|
105 | case keyCode.DOWN:
|
---|
106 | suppressKeyPress = true;
|
---|
107 | this._keyEvent( "next", event );
|
---|
108 | break;
|
---|
109 | case keyCode.ENTER:
|
---|
110 | case keyCode.NUMPAD_ENTER:
|
---|
111 | // when menu is open and has focus
|
---|
112 | if ( this.menu.active ) {
|
---|
113 | // #6055 - Opera still allows the keypress to occur
|
---|
114 | // which causes forms to submit
|
---|
115 | suppressKeyPress = true;
|
---|
116 | event.preventDefault();
|
---|
117 | this.menu.select( event );
|
---|
118 | }
|
---|
119 | break;
|
---|
120 | case keyCode.TAB:
|
---|
121 | if ( this.menu.active ) {
|
---|
122 | this.menu.select( event );
|
---|
123 | }
|
---|
124 | break;
|
---|
125 | case keyCode.ESCAPE:
|
---|
126 | if ( this.menu.element.is( ":visible" ) ) {
|
---|
127 | this._value( this.term );
|
---|
128 | this.close( event );
|
---|
129 | // Different browsers have different default behavior for escape
|
---|
130 | // Single press can mean undo or clear
|
---|
131 | // Double press in IE means clear the whole form
|
---|
132 | event.preventDefault();
|
---|
133 | }
|
---|
134 | break;
|
---|
135 | default:
|
---|
136 | suppressKeyPressRepeat = true;
|
---|
137 | // search timeout should be triggered before the input value is changed
|
---|
138 | this._searchTimeout( event );
|
---|
139 | break;
|
---|
140 | }
|
---|
141 | },
|
---|
142 | keypress: function( event ) {
|
---|
143 | if ( suppressKeyPress ) {
|
---|
144 | suppressKeyPress = false;
|
---|
145 | if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
|
---|
146 | event.preventDefault();
|
---|
147 | }
|
---|
148 | return;
|
---|
149 | }
|
---|
150 | if ( suppressKeyPressRepeat ) {
|
---|
151 | return;
|
---|
152 | }
|
---|
153 |
|
---|
154 | // replicate some key handlers to allow them to repeat in Firefox and Opera
|
---|
155 | var keyCode = $.ui.keyCode;
|
---|
156 | switch( event.keyCode ) {
|
---|
157 | case keyCode.PAGE_UP:
|
---|
158 | this._move( "previousPage", event );
|
---|
159 | break;
|
---|
160 | case keyCode.PAGE_DOWN:
|
---|
161 | this._move( "nextPage", event );
|
---|
162 | break;
|
---|
163 | case keyCode.UP:
|
---|
164 | this._keyEvent( "previous", event );
|
---|
165 | break;
|
---|
166 | case keyCode.DOWN:
|
---|
167 | this._keyEvent( "next", event );
|
---|
168 | break;
|
---|
169 | }
|
---|
170 | },
|
---|
171 | input: function( event ) {
|
---|
172 | if ( suppressInput ) {
|
---|
173 | suppressInput = false;
|
---|
174 | event.preventDefault();
|
---|
175 | return;
|
---|
176 | }
|
---|
177 | this._searchTimeout( event );
|
---|
178 | },
|
---|
179 | focus: function() {
|
---|
180 | this.selectedItem = null;
|
---|
181 | this.previous = this._value();
|
---|
182 | },
|
---|
183 | blur: function( event ) {
|
---|
184 | if ( this.cancelBlur ) {
|
---|
185 | delete this.cancelBlur;
|
---|
186 | return;
|
---|
187 | }
|
---|
188 |
|
---|
189 | clearTimeout( this.searching );
|
---|
190 | this.close( event );
|
---|
191 | this._change( event );
|
---|
192 | }
|
---|
193 | });
|
---|
194 |
|
---|
195 | this._initSource();
|
---|
196 | this.menu = $( "<ul>" )
|
---|
197 | .addClass( "ui-autocomplete ui-front" )
|
---|
198 | .appendTo( this._appendTo() )
|
---|
199 | .menu({
|
---|
200 | // disable ARIA support, the live region takes care of that
|
---|
201 | role: null
|
---|
202 | })
|
---|
203 | .hide()
|
---|
204 | .data( "ui-menu" );
|
---|
205 |
|
---|
206 | this._on( this.menu.element, {
|
---|
207 | mousedown: function( event ) {
|
---|
208 | // prevent moving focus out of the text field
|
---|
209 | event.preventDefault();
|
---|
210 |
|
---|
211 | // IE doesn't prevent moving focus even with event.preventDefault()
|
---|
212 | // so we set a flag to know when we should ignore the blur event
|
---|
213 | this.cancelBlur = true;
|
---|
214 | this._delay(function() {
|
---|
215 | delete this.cancelBlur;
|
---|
216 | });
|
---|
217 |
|
---|
218 | // clicking on the scrollbar causes focus to shift to the body
|
---|
219 | // but we can't detect a mouseup or a click immediately afterward
|
---|
220 | // so we have to track the next mousedown and close the menu if
|
---|
221 | // the user clicks somewhere outside of the autocomplete
|
---|
222 | var menuElement = this.menu.element[ 0 ];
|
---|
223 | if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
|
---|
224 | this._delay(function() {
|
---|
225 | var that = this;
|
---|
226 | this.document.one( "mousedown", function( event ) {
|
---|
227 | if ( event.target !== that.element[ 0 ] &&
|
---|
228 | event.target !== menuElement &&
|
---|
229 | !$.contains( menuElement, event.target ) ) {
|
---|
230 | that.close();
|
---|
231 | }
|
---|
232 | });
|
---|
233 | });
|
---|
234 | }
|
---|
235 | },
|
---|
236 | menufocus: function( event, ui ) {
|
---|
237 | // support: Firefox
|
---|
238 | // Prevent accidental activation of menu items in Firefox (#7024 #9118)
|
---|
239 | if ( this.isNewMenu ) {
|
---|
240 | this.isNewMenu = false;
|
---|
241 | if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
|
---|
242 | this.menu.blur();
|
---|
243 |
|
---|
244 | this.document.one( "mousemove", function() {
|
---|
245 | $( event.target ).trigger( event.originalEvent );
|
---|
246 | });
|
---|
247 |
|
---|
248 | return;
|
---|
249 | }
|
---|
250 | }
|
---|
251 |
|
---|
252 | var item = ui.item.data( "ui-autocomplete-item" );
|
---|
253 | if ( false !== this._trigger( "focus", event, { item: item } ) ) {
|
---|
254 | // use value to match what will end up in the input, if it was a key event
|
---|
255 | if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
|
---|
256 | this._value( item.value );
|
---|
257 | }
|
---|
258 | } else {
|
---|
259 | // Normally the input is populated with the item's value as the
|
---|
260 | // menu is navigated, causing screen readers to notice a change and
|
---|
261 | // announce the item. Since the focus event was canceled, this doesn't
|
---|
262 | // happen, so we update the live region so that screen readers can
|
---|
263 | // still notice the change and announce it.
|
---|
264 | this.liveRegion.text( item.value );
|
---|
265 | }
|
---|
266 | },
|
---|
267 | menuselect: function( event, ui ) {
|
---|
268 | var item = ui.item.data( "ui-autocomplete-item" ),
|
---|
269 | previous = this.previous;
|
---|
270 |
|
---|
271 | // only trigger when focus was lost (click on menu)
|
---|
272 | if ( this.element[0] !== this.document[0].activeElement ) {
|
---|
273 | this.element.focus();
|
---|
274 | this.previous = previous;
|
---|
275 | // #6109 - IE triggers two focus events and the second
|
---|
276 | // is asynchronous, so we need to reset the previous
|
---|
277 | // term synchronously and asynchronously :-(
|
---|
278 | this._delay(function() {
|
---|
279 | this.previous = previous;
|
---|
280 | this.selectedItem = item;
|
---|
281 | });
|
---|
282 | }
|
---|
283 |
|
---|
284 | if ( false !== this._trigger( "select", event, { item: item } ) ) {
|
---|
285 | this._value( item.value );
|
---|
286 | }
|
---|
287 | // reset the term after the select event
|
---|
288 | // this allows custom select handling to work properly
|
---|
289 | this.term = this._value();
|
---|
290 |
|
---|
291 | this.close( event );
|
---|
292 | this.selectedItem = item;
|
---|
293 | }
|
---|
294 | });
|
---|
295 |
|
---|
296 | this.liveRegion = $( "<span>", {
|
---|
297 | role: "status",
|
---|
298 | "aria-live": "polite"
|
---|
299 | })
|
---|
300 | .addClass( "ui-helper-hidden-accessible" )
|
---|
301 | .insertBefore( this.element );
|
---|
302 |
|
---|
303 | // turning off autocomplete prevents the browser from remembering the
|
---|
304 | // value when navigating through history, so we re-enable autocomplete
|
---|
305 | // if the page is unloaded before the widget is destroyed. #7790
|
---|
306 | this._on( this.window, {
|
---|
307 | beforeunload: function() {
|
---|
308 | this.element.removeAttr( "autocomplete" );
|
---|
309 | }
|
---|
310 | });
|
---|
311 | },
|
---|
312 |
|
---|
313 | _destroy: function() {
|
---|
314 | clearTimeout( this.searching );
|
---|
315 | this.element
|
---|
316 | .removeClass( "ui-autocomplete-input" )
|
---|
317 | .removeAttr( "autocomplete" );
|
---|
318 | this.menu.element.remove();
|
---|
319 | this.liveRegion.remove();
|
---|
320 | },
|
---|
321 |
|
---|
322 | _setOption: function( key, value ) {
|
---|
323 | this._super( key, value );
|
---|
324 | if ( key === "source" ) {
|
---|
325 | this._initSource();
|
---|
326 | }
|
---|
327 | if ( key === "appendTo" ) {
|
---|
328 | this.menu.element.appendTo( this._appendTo() );
|
---|
329 | }
|
---|
330 | if ( key === "disabled" && value && this.xhr ) {
|
---|
331 | this.xhr.abort();
|
---|
332 | }
|
---|
333 | },
|
---|
334 |
|
---|
335 | _appendTo: function() {
|
---|
336 | var element = this.options.appendTo;
|
---|
337 |
|
---|
338 | if ( element ) {
|
---|
339 | element = element.jquery || element.nodeType ?
|
---|
340 | $( element ) :
|
---|
341 | this.document.find( element ).eq( 0 );
|
---|
342 | }
|
---|
343 |
|
---|
344 | if ( !element ) {
|
---|
345 | element = this.element.closest( ".ui-front" );
|
---|
346 | }
|
---|
347 |
|
---|
348 | if ( !element.length ) {
|
---|
349 | element = this.document[0].body;
|
---|
350 | }
|
---|
351 |
|
---|
352 | return element;
|
---|
353 | },
|
---|
354 |
|
---|
355 | _initSource: function() {
|
---|
356 | var array, url,
|
---|
357 | that = this;
|
---|
358 | if ( $.isArray(this.options.source) ) {
|
---|
359 | array = this.options.source;
|
---|
360 | this.source = function( request, response ) {
|
---|
361 | response( $.ui.autocomplete.filter( array, request.term ) );
|
---|
362 | };
|
---|
363 | } else if ( typeof this.options.source === "string" ) {
|
---|
364 | url = this.options.source;
|
---|
365 | this.source = function( request, response ) {
|
---|
366 | if ( that.xhr ) {
|
---|
367 | that.xhr.abort();
|
---|
368 | }
|
---|
369 | that.xhr = $.ajax({
|
---|
370 | url: url,
|
---|
371 | data: request,
|
---|
372 | dataType: "json",
|
---|
373 | success: function( data ) {
|
---|
374 | response( data );
|
---|
375 | },
|
---|
376 | error: function() {
|
---|
377 | response( [] );
|
---|
378 | }
|
---|
379 | });
|
---|
380 | };
|
---|
381 | } else {
|
---|
382 | this.source = this.options.source;
|
---|
383 | }
|
---|
384 | },
|
---|
385 |
|
---|
386 | _searchTimeout: function( event ) {
|
---|
387 | clearTimeout( this.searching );
|
---|
388 | this.searching = this._delay(function() {
|
---|
389 | // only search if the value has changed
|
---|
390 | if ( this.term !== this._value() ) {
|
---|
391 | this.selectedItem = null;
|
---|
392 | this.search( null, event );
|
---|
393 | }
|
---|
394 | }, this.options.delay );
|
---|
395 | },
|
---|
396 |
|
---|
397 | search: function( value, event ) {
|
---|
398 | value = value != null ? value : this._value();
|
---|
399 |
|
---|
400 | // always save the actual value, not the one passed as an argument
|
---|
401 | this.term = this._value();
|
---|
402 |
|
---|
403 | if ( value.length < this.options.minLength ) {
|
---|
404 | return this.close( event );
|
---|
405 | }
|
---|
406 |
|
---|
407 | if ( this._trigger( "search", event ) === false ) {
|
---|
408 | return;
|
---|
409 | }
|
---|
410 |
|
---|
411 | return this._search( value );
|
---|
412 | },
|
---|
413 |
|
---|
414 | _search: function( value ) {
|
---|
415 | this.pending++;
|
---|
416 | this.element.addClass( "ui-autocomplete-loading" );
|
---|
417 | this.cancelSearch = false;
|
---|
418 |
|
---|
419 | this.source( { term: value }, this._response() );
|
---|
420 | },
|
---|
421 |
|
---|
422 | _response: function() {
|
---|
423 | var that = this,
|
---|
424 | index = ++requestIndex;
|
---|
425 |
|
---|
426 | return function( content ) {
|
---|
427 | if ( index === requestIndex ) {
|
---|
428 | that.__response( content );
|
---|
429 | }
|
---|
430 |
|
---|
431 | that.pending--;
|
---|
432 | if ( !that.pending ) {
|
---|
433 | that.element.removeClass( "ui-autocomplete-loading" );
|
---|
434 | }
|
---|
435 | };
|
---|
436 | },
|
---|
437 |
|
---|
438 | __response: function( content ) {
|
---|
439 | if ( content ) {
|
---|
440 | content = this._normalize( content );
|
---|
441 | }
|
---|
442 | this._trigger( "response", null, { content: content } );
|
---|
443 | if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
|
---|
444 | this._suggest( content );
|
---|
445 | this._trigger( "open" );
|
---|
446 | } else {
|
---|
447 | // use ._close() instead of .close() so we don't cancel future searches
|
---|
448 | this._close();
|
---|
449 | }
|
---|
450 | },
|
---|
451 |
|
---|
452 | close: function( event ) {
|
---|
453 | this.cancelSearch = true;
|
---|
454 | this._close( event );
|
---|
455 | },
|
---|
456 |
|
---|
457 | _close: function( event ) {
|
---|
458 | if ( this.menu.element.is( ":visible" ) ) {
|
---|
459 | this.menu.element.hide();
|
---|
460 | this.menu.blur();
|
---|
461 | this.isNewMenu = true;
|
---|
462 | this._trigger( "close", event );
|
---|
463 | }
|
---|
464 | },
|
---|
465 |
|
---|
466 | _change: function( event ) {
|
---|
467 | if ( this.previous !== this._value() ) {
|
---|
468 | this._trigger( "change", event, { item: this.selectedItem } );
|
---|
469 | }
|
---|
470 | },
|
---|
471 |
|
---|
472 | _normalize: function( items ) {
|
---|
473 | // assume all items have the right format when the first item is complete
|
---|
474 | if ( items.length && items[0].label && items[0].value ) {
|
---|
475 | return items;
|
---|
476 | }
|
---|
477 | return $.map( items, function( item ) {
|
---|
478 | if ( typeof item === "string" ) {
|
---|
479 | return {
|
---|
480 | label: item,
|
---|
481 | value: item
|
---|
482 | };
|
---|
483 | }
|
---|
484 | return $.extend({
|
---|
485 | label: item.label || item.value,
|
---|
486 | value: item.value || item.label
|
---|
487 | }, item );
|
---|
488 | });
|
---|
489 | },
|
---|
490 |
|
---|
491 | _suggest: function( items ) {
|
---|
492 | var ul = this.menu.element.empty();
|
---|
493 | this._renderMenu( ul, items );
|
---|
494 | this.isNewMenu = true;
|
---|
495 | this.menu.refresh();
|
---|
496 |
|
---|
497 | // size and position menu
|
---|
498 | ul.show();
|
---|
499 | this._resizeMenu();
|
---|
500 | ul.position( $.extend({
|
---|
501 | of: this.element
|
---|
502 | }, this.options.position ));
|
---|
503 |
|
---|
504 | if ( this.options.autoFocus ) {
|
---|
505 | this.menu.next();
|
---|
506 | }
|
---|
507 | },
|
---|
508 |
|
---|
509 | _resizeMenu: function() {
|
---|
510 | var ul = this.menu.element;
|
---|
511 | ul.outerWidth( Math.max(
|
---|
512 | // Firefox wraps long text (possibly a rounding bug)
|
---|
513 | // so we add 1px to avoid the wrapping (#7513)
|
---|
514 | ul.width( "" ).outerWidth() + 1,
|
---|
515 | this.element.outerWidth()
|
---|
516 | ) );
|
---|
517 | },
|
---|
518 |
|
---|
519 | _renderMenu: function( ul, items ) {
|
---|
520 | var that = this;
|
---|
521 | $.each( items, function( index, item ) {
|
---|
522 | that._renderItemData( ul, item );
|
---|
523 | });
|
---|
524 | },
|
---|
525 |
|
---|
526 | _renderItemData: function( ul, item ) {
|
---|
527 | return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
|
---|
528 | },
|
---|
529 |
|
---|
530 | _renderItem: function( ul, item ) {
|
---|
531 | return $( "<li>" )
|
---|
532 | .append( $( "<a>" ).text( item.label ) )
|
---|
533 | .appendTo( ul );
|
---|
534 | },
|
---|
535 |
|
---|
536 | _move: function( direction, event ) {
|
---|
537 | if ( !this.menu.element.is( ":visible" ) ) {
|
---|
538 | this.search( null, event );
|
---|
539 | return;
|
---|
540 | }
|
---|
541 | if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
|
---|
542 | this.menu.isLastItem() && /^next/.test( direction ) ) {
|
---|
543 | this._value( this.term );
|
---|
544 | this.menu.blur();
|
---|
545 | return;
|
---|
546 | }
|
---|
547 | this.menu[ direction ]( event );
|
---|
548 | },
|
---|
549 |
|
---|
550 | widget: function() {
|
---|
551 | return this.menu.element;
|
---|
552 | },
|
---|
553 |
|
---|
554 | _value: function() {
|
---|
555 | return this.valueMethod.apply( this.element, arguments );
|
---|
556 | },
|
---|
557 |
|
---|
558 | _keyEvent: function( keyEvent, event ) {
|
---|
559 | if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
|
---|
560 | this._move( keyEvent, event );
|
---|
561 |
|
---|
562 | // prevents moving cursor to beginning/end of the text field in some browsers
|
---|
563 | event.preventDefault();
|
---|
564 | }
|
---|
565 | }
|
---|
566 | });
|
---|
567 |
|
---|
568 | $.extend( $.ui.autocomplete, {
|
---|
569 | escapeRegex: function( value ) {
|
---|
570 | return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
---|
571 | },
|
---|
572 | filter: function(array, term) {
|
---|
573 | var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
|
---|
574 | return $.grep( array, function(value) {
|
---|
575 | return matcher.test( value.label || value.value || value );
|
---|
576 | });
|
---|
577 | }
|
---|
578 | });
|
---|
579 |
|
---|
580 |
|
---|
581 | // live region extension, adding a `messages` option
|
---|
582 | // NOTE: This is an experimental API. We are still investigating
|
---|
583 | // a full solution for string manipulation and internationalization.
|
---|
584 | $.widget( "ui.autocomplete", $.ui.autocomplete, {
|
---|
585 | options: {
|
---|
586 | messages: {
|
---|
587 | noResults: "No search results.",
|
---|
588 | results: function( amount ) {
|
---|
589 | return amount + ( amount > 1 ? " results are" : " result is" ) +
|
---|
590 | " available, use up and down arrow keys to navigate.";
|
---|
591 | }
|
---|
592 | }
|
---|
593 | },
|
---|
594 |
|
---|
595 | __response: function( content ) {
|
---|
596 | var message;
|
---|
597 | this._superApply( arguments );
|
---|
598 | if ( this.options.disabled || this.cancelSearch ) {
|
---|
599 | return;
|
---|
600 | }
|
---|
601 | if ( content && content.length ) {
|
---|
602 | message = this.options.messages.results( content.length );
|
---|
603 | } else {
|
---|
604 | message = this.options.messages.noResults;
|
---|
605 | }
|
---|
606 | this.liveRegion.text( message );
|
---|
607 | }
|
---|
608 | });
|
---|
609 |
|
---|
610 | }( jQuery ));
|
---|