[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /*! 2 * jQuery UI Autocomplete 1.13.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 "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 "./core" 29 ], factory ); 30 } else { 31 32 // Browser globals 33 factory( jQuery ); 34 } 35 } )( function( $ ) { 36 "use strict"; 37 38 $.widget( "ui.autocomplete", { 39 version: "1.13.1", 40 defaultElement: "<input>", 41 options: { 42 appendTo: null, 43 autoFocus: false, 44 delay: 300, 45 minLength: 1, 46 position: { 47 my: "left top", 48 at: "left bottom", 49 collision: "none" 50 }, 51 source: null, 52 53 // Callbacks 54 change: null, 55 close: null, 56 focus: null, 57 open: null, 58 response: null, 59 search: null, 60 select: null 61 }, 62 63 requestIndex: 0, 64 pending: 0, 65 liveRegionTimer: null, 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 clearTimeout( this.searching ); 204 this.close( event ); 205 this._change( event ); 206 } 207 } ); 208 209 this._initSource(); 210 this.menu = $( "<ul>" ) 211 .appendTo( this._appendTo() ) 212 .menu( { 213 214 // disable ARIA support, the live region takes care of that 215 role: null 216 } ) 217 .hide() 218 219 // Support: IE 11 only, Edge <= 14 220 // For other browsers, we preventDefault() on the mousedown event 221 // to keep the dropdown from taking focus from the input. This doesn't 222 // work for IE/Edge, causing problems with selection and scrolling (#9638) 223 // Happily, IE and Edge support an "unselectable" attribute that 224 // prevents an element from receiving focus, exactly what we want here. 225 .attr( { 226 "unselectable": "on" 227 } ) 228 .menu( "instance" ); 229 230 this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); 231 this._on( this.menu.element, { 232 mousedown: function( event ) { 233 234 // Prevent moving focus out of the text field 235 event.preventDefault(); 236 }, 237 menufocus: function( event, ui ) { 238 var label, item; 239 240 // support: Firefox 241 // Prevent accidental activation of menu items in Firefox (#7024 #9118) 242 if ( this.isNewMenu ) { 243 this.isNewMenu = false; 244 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { 245 this.menu.blur(); 246 247 this.document.one( "mousemove", function() { 248 $( event.target ).trigger( event.originalEvent ); 249 } ); 250 251 return; 252 } 253 } 254 255 item = ui.item.data( "ui-autocomplete-item" ); 256 if ( false !== this._trigger( "focus", event, { item: item } ) ) { 257 258 // use value to match what will end up in the input, if it was a key event 259 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { 260 this._value( item.value ); 261 } 262 } 263 264 // Announce the value in the liveRegion 265 label = ui.item.attr( "aria-label" ) || item.value; 266 if ( label && String.prototype.trim.call( label ).length ) { 267 clearTimeout( this.liveRegionTimer ); 268 this.liveRegionTimer = this._delay( function() { 269 this.liveRegion.html( $( "<div>" ).text( label ) ); 270 }, 100 ); 271 } 272 }, 273 menuselect: function( event, ui ) { 274 var item = ui.item.data( "ui-autocomplete-item" ), 275 previous = this.previous; 276 277 // Only trigger when focus was lost (click on menu) 278 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { 279 this.element.trigger( "focus" ); 280 this.previous = previous; 281 282 // #6109 - IE triggers two focus events and the second 283 // is asynchronous, so we need to reset the previous 284 // term synchronously and asynchronously :-( 285 this._delay( function() { 286 this.previous = previous; 287 this.selectedItem = item; 288 } ); 289 } 290 291 if ( false !== this._trigger( "select", event, { item: item } ) ) { 292 this._value( item.value ); 293 } 294 295 // reset the term after the select event 296 // this allows custom select handling to work properly 297 this.term = this._value(); 298 299 this.close( event ); 300 this.selectedItem = item; 301 } 302 } ); 303 304 this.liveRegion = $( "<div>", { 305 role: "status", 306 "aria-live": "assertive", 307 "aria-relevant": "additions" 308 } ) 309 .appendTo( this.document[ 0 ].body ); 310 311 this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); 312 313 // Turning off autocomplete prevents the browser from remembering the 314 // value when navigating through history, so we re-enable autocomplete 315 // if the page is unloaded before the widget is destroyed. #7790 316 this._on( this.window, { 317 beforeunload: function() { 318 this.element.removeAttr( "autocomplete" ); 319 } 320 } ); 321 }, 322 323 _destroy: function() { 324 clearTimeout( this.searching ); 325 this.element.removeAttr( "autocomplete" ); 326 this.menu.element.remove(); 327 this.liveRegion.remove(); 328 }, 329 330 _setOption: function( key, value ) { 331 this._super( key, value ); 332 if ( key === "source" ) { 333 this._initSource(); 334 } 335 if ( key === "appendTo" ) { 336 this.menu.element.appendTo( this._appendTo() ); 337 } 338 if ( key === "disabled" && value && this.xhr ) { 339 this.xhr.abort(); 340 } 341 }, 342 343 _isEventTargetInWidget: function( event ) { 344 var menuElement = this.menu.element[ 0 ]; 345 346 return event.target === this.element[ 0 ] || 347 event.target === menuElement || 348 $.contains( menuElement, event.target ); 349 }, 350 351 _closeOnClickOutside: function( event ) { 352 if ( !this._isEventTargetInWidget( event ) ) { 353 this.close(); 354 } 355 }, 356 357 _appendTo: function() { 358 var element = this.options.appendTo; 359 360 if ( element ) { 361 element = element.jquery || element.nodeType ? 362 $( element ) : 363 this.document.find( element ).eq( 0 ); 364 } 365 366 if ( !element || !element[ 0 ] ) { 367 element = this.element.closest( ".ui-front, dialog" ); 368 } 369 370 if ( !element.length ) { 371 element = this.document[ 0 ].body; 372 } 373 374 return element; 375 }, 376 377 _initSource: function() { 378 var array, url, 379 that = this; 380 if ( Array.isArray( this.options.source ) ) { 381 array = this.options.source; 382 this.source = function( request, response ) { 383 response( $.ui.autocomplete.filter( array, request.term ) ); 384 }; 385 } else if ( typeof this.options.source === "string" ) { 386 url = this.options.source; 387 this.source = function( request, response ) { 388 if ( that.xhr ) { 389 that.xhr.abort(); 390 } 391 that.xhr = $.ajax( { 392 url: url, 393 data: request, 394 dataType: "json", 395 success: function( data ) { 396 response( data ); 397 }, 398 error: function() { 399 response( [] ); 400 } 401 } ); 402 }; 403 } else { 404 this.source = this.options.source; 405 } 406 }, 407 408 _searchTimeout: function( event ) { 409 clearTimeout( this.searching ); 410 this.searching = this._delay( function() { 411 412 // Search if the value has changed, or if the user retypes the same value (see #7434) 413 var equalValues = this.term === this._value(), 414 menuVisible = this.menu.element.is( ":visible" ), 415 modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; 416 417 if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) { 418 this.selectedItem = null; 419 this.search( null, event ); 420 } 421 }, this.options.delay ); 422 }, 423 424 search: function( value, event ) { 425 value = value != null ? value : this._value(); 426 427 // Always save the actual value, not the one passed as an argument 428 this.term = this._value(); 429 430 if ( value.length < this.options.minLength ) { 431 return this.close( event ); 432 } 433 434 if ( this._trigger( "search", event ) === false ) { 435 return; 436 } 437 438 return this._search( value ); 439 }, 440 441 _search: function( value ) { 442 this.pending++; 443 this._addClass( "ui-autocomplete-loading" ); 444 this.cancelSearch = false; 445 446 this.source( { term: value }, this._response() ); 447 }, 448 449 _response: function() { 450 var index = ++this.requestIndex; 451 452 return function( content ) { 453 if ( index === this.requestIndex ) { 454 this.__response( content ); 455 } 456 457 this.pending--; 458 if ( !this.pending ) { 459 this._removeClass( "ui-autocomplete-loading" ); 460 } 461 }.bind( this ); 462 }, 463 464 __response: function( content ) { 465 if ( content ) { 466 content = this._normalize( content ); 467 } 468 this._trigger( "response", null, { content: content } ); 469 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { 470 this._suggest( content ); 471 this._trigger( "open" ); 472 } else { 473 474 // use ._close() instead of .close() so we don't cancel future searches 475 this._close(); 476 } 477 }, 478 479 close: function( event ) { 480 this.cancelSearch = true; 481 this._close( event ); 482 }, 483 484 _close: function( event ) { 485 486 // Remove the handler that closes the menu on outside clicks 487 this._off( this.document, "mousedown" ); 488 489 if ( this.menu.element.is( ":visible" ) ) { 490 this.menu.element.hide(); 491 this.menu.blur(); 492 this.isNewMenu = true; 493 this._trigger( "close", event ); 494 } 495 }, 496 497 _change: function( event ) { 498 if ( this.previous !== this._value() ) { 499 this._trigger( "change", event, { item: this.selectedItem } ); 500 } 501 }, 502 503 _normalize: function( items ) { 504 505 // assume all items have the right format when the first item is complete 506 if ( items.length && items[ 0 ].label && items[ 0 ].value ) { 507 return items; 508 } 509 return $.map( items, function( item ) { 510 if ( typeof item === "string" ) { 511 return { 512 label: item, 513 value: item 514 }; 515 } 516 return $.extend( {}, item, { 517 label: item.label || item.value, 518 value: item.value || item.label 519 } ); 520 } ); 521 }, 522 523 _suggest: function( items ) { 524 var ul = this.menu.element.empty(); 525 this._renderMenu( ul, items ); 526 this.isNewMenu = true; 527 this.menu.refresh(); 528 529 // Size and position menu 530 ul.show(); 531 this._resizeMenu(); 532 ul.position( $.extend( { 533 of: this.element 534 }, this.options.position ) ); 535 536 if ( this.options.autoFocus ) { 537 this.menu.next(); 538 } 539 540 // Listen for interactions outside of the widget (#6642) 541 this._on( this.document, { 542 mousedown: "_closeOnClickOutside" 543 } ); 544 }, 545 546 _resizeMenu: function() { 547 var ul = this.menu.element; 548 ul.outerWidth( Math.max( 549 550 // Firefox wraps long text (possibly a rounding bug) 551 // so we add 1px to avoid the wrapping (#7513) 552 ul.width( "" ).outerWidth() + 1, 553 this.element.outerWidth() 554 ) ); 555 }, 556 557 _renderMenu: function( ul, items ) { 558 var that = this; 559 $.each( items, function( index, item ) { 560 that._renderItemData( ul, item ); 561 } ); 562 }, 563 564 _renderItemData: function( ul, item ) { 565 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); 566 }, 567 568 _renderItem: function( ul, item ) { 569 return $( "<li>" ) 570 .append( $( "<div>" ).text( item.label ) ) 571 .appendTo( ul ); 572 }, 573 574 _move: function( direction, event ) { 575 if ( !this.menu.element.is( ":visible" ) ) { 576 this.search( null, event ); 577 return; 578 } 579 if ( this.menu.isFirstItem() && /^previous/.test( direction ) || 580 this.menu.isLastItem() && /^next/.test( direction ) ) { 581 582 if ( !this.isMultiLine ) { 583 this._value( this.term ); 584 } 585 586 this.menu.blur(); 587 return; 588 } 589 this.menu[ direction ]( event ); 590 }, 591 592 widget: function() { 593 return this.menu.element; 594 }, 595 596 _value: function() { 597 return this.valueMethod.apply( this.element, arguments ); 598 }, 599 600 _keyEvent: function( keyEvent, event ) { 601 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { 602 this._move( keyEvent, event ); 603 604 // Prevents moving cursor to beginning/end of the text field in some browsers 605 event.preventDefault(); 606 } 607 }, 608 609 // Support: Chrome <=50 610 // We should be able to just use this.element.prop( "isContentEditable" ) 611 // but hidden elements always report false in Chrome. 612 // https://code.google.com/p/chromium/issues/detail?id=313082 613 _isContentEditable: function( element ) { 614 if ( !element.length ) { 615 return false; 616 } 617 618 var editable = element.prop( "contentEditable" ); 619 620 if ( editable === "inherit" ) { 621 return this._isContentEditable( element.parent() ); 622 } 623 624 return editable === "true"; 625 } 626 } ); 627 628 $.extend( $.ui.autocomplete, { 629 escapeRegex: function( value ) { 630 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); 631 }, 632 filter: function( array, term ) { 633 var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" ); 634 return $.grep( array, function( value ) { 635 return matcher.test( value.label || value.value || value ); 636 } ); 637 } 638 } ); 639 640 // Live region extension, adding a `messages` option 641 // NOTE: This is an experimental API. We are still investigating 642 // a full solution for string manipulation and internationalization. 643 $.widget( "ui.autocomplete", $.ui.autocomplete, { 644 options: { 645 messages: { 646 noResults: "No search results.", 647 results: function( amount ) { 648 return amount + ( amount > 1 ? " results are" : " result is" ) + 649 " available, use up and down arrow keys to navigate."; 650 } 651 } 652 }, 653 654 __response: function( content ) { 655 var message; 656 this._superApply( arguments ); 657 if ( this.options.disabled || this.cancelSearch ) { 658 return; 659 } 660 if ( content && content.length ) { 661 message = this.options.messages.results( content.length ); 662 } else { 663 message = this.options.messages.noResults; 664 } 665 clearTimeout( this.liveRegionTimer ); 666 this.liveRegionTimer = this._delay( function() { 667 this.liveRegion.html( $( "<div>" ).text( message ) ); 668 }, 100 ); 669 } 670 } ); 671 672 return $.ui.autocomplete; 673 674 } );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |