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