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