corrade-http-templates – Blame information for rev

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