1/* 2 * jQuery UI Autocomplete 1.8.18 3 * 4 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 5 * Dual licensed under the MIT or GPL Version 2 licenses. 6 * http://jquery.org/license 7 * 8 * http://docs.jquery.com/UI/Autocomplete 9 * 10 * Depends: 11 * jquery.ui.core.js 12 * jquery.ui.widget.js 13 * jquery.ui.position.js 14 */ 15(function( $, undefined ) { 16 17// used to prevent race conditions with remote data sources 18var requestIndex = 0; 19 20$.widget( "ui.autocomplete", { 21 options: { 22 appendTo: "body", 23 autoFocus: false, 24 delay: 300, 25 minLength: 1, 26 position: { 27 my: "left top", 28 at: "left bottom", 29 collision: "none" 30 }, 31 source: null 32 }, 33 34 pending: 0, 35 36 _create: function() { 37 var self = this, 38 doc = this.element[ 0 ].ownerDocument, 39 suppressKeyPress; 40 41 this.element 42 .addClass( "ui-autocomplete-input" ) 43 .attr( "autocomplete", "off" ) 44 // TODO verify these actually work as intended 45 .attr({ 46 role: "textbox", 47 "aria-autocomplete": "list", 48 "aria-haspopup": "true" 49 }) 50 .bind( "keydown.autocomplete", function( event ) { 51 if ( self.options.disabled || self.element.propAttr( "readOnly" ) ) { 52 return; 53 } 54 55 suppressKeyPress = false; 56 var keyCode = $.ui.keyCode; 57 switch( event.keyCode ) { 58 case keyCode.PAGE_UP: 59 self._move( "previousPage", event ); 60 break; 61 case keyCode.PAGE_DOWN: 62 self._move( "nextPage", event ); 63 break; 64 case keyCode.UP: 65 self._move( "previous", event ); 66 // prevent moving cursor to beginning of text field in some browsers 67 event.preventDefault(); 68 break; 69 case keyCode.DOWN: 70 self._move( "next", event ); 71 // prevent moving cursor to end of text field in some browsers 72 event.preventDefault(); 73 break; 74 case keyCode.ENTER: 75 case keyCode.NUMPAD_ENTER: 76 // when menu is open and has focus 77 if ( self.menu.active ) { 78 // #6055 - Opera still allows the keypress to occur 79 // which causes forms to submit 80 suppressKeyPress = true; 81 event.preventDefault(); 82 } 83 //passthrough - ENTER and TAB both select the current element 84 case keyCode.TAB: 85 if ( !self.menu.active ) { 86 return; 87 } 88 self.menu.select( event ); 89 break; 90 case keyCode.ESCAPE: 91 self.element.val( self.term ); 92 self.close( event ); 93 break; 94 default: 95 // keypress is triggered before the input value is changed 96 clearTimeout( self.searching ); 97 self.searching = setTimeout(function() { 98 // only search if the value has changed 99 if ( self.term != self.element.val() ) { 100 self.selectedItem = null; 101 self.search( null, event ); 102 } 103 }, self.options.delay ); 104 break; 105 } 106 }) 107 .bind( "keypress.autocomplete", function( event ) { 108 if ( suppressKeyPress ) { 109 suppressKeyPress = false; 110 event.preventDefault(); 111 } 112 }) 113 .bind( "focus.autocomplete", function() { 114 if ( self.options.disabled ) { 115 return; 116 } 117 118 self.selectedItem = null; 119 self.previous = self.element.val(); 120 }) 121 .bind( "blur.autocomplete", function( event ) { 122 if ( self.options.disabled ) { 123 return; 124 } 125 126 clearTimeout( self.searching ); 127 // clicks on the menu (or a button to trigger a search) will cause a blur event 128 self.closing = setTimeout(function() { 129 self.close( event ); 130 self._change( event ); 131 }, 150 ); 132 }); 133 this._initSource(); 134 this.response = function() { 135 return self._response.apply( self, arguments ); 136 }; 137 this.menu = $( "<ul></ul>" ) 138 .addClass( "ui-autocomplete" ) 139 .appendTo( $( this.options.appendTo || "body", doc )[0] ) 140 // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown) 141 .mousedown(function( event ) { 142 // clicking on the scrollbar causes focus to shift to the body 143 // but we can't detect a mouseup or a click immediately afterward 144 // so we have to track the next mousedown and close the menu if 145 // the user clicks somewhere outside of the autocomplete 146 var menuElement = self.menu.element[ 0 ]; 147 if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { 148 setTimeout(function() { 149 $( document ).one( 'mousedown', function( event ) { 150 if ( event.target !== self.element[ 0 ] && 151 event.target !== menuElement && 152 !$.ui.contains( menuElement, event.target ) ) { 153 self.close(); 154 } 155 }); 156 }, 1 ); 157 } 158 159 // use another timeout to make sure the blur-event-handler on the input was already triggered 160 setTimeout(function() { 161 clearTimeout( self.closing ); 162 }, 13); 163 }) 164 .menu({ 165 focus: function( event, ui ) { 166 var item = ui.item.data( "item.autocomplete" ); 167 if ( false !== self._trigger( "focus", event, { item: item } ) ) { 168 // use value to match what will end up in the input, if it was a key event 169 if ( /^key/.test(event.originalEvent.type) ) { 170 self.element.val( item.value ); 171 } 172 } 173 }, 174 selected: function( event, ui ) { 175 var item = ui.item.data( "item.autocomplete" ), 176 previous = self.previous; 177
178 // only trigger when focus was lost (click on menu) 179 if ( self.element[0] !== doc.activeElement ) { 180 self.element.focus(); 181 self.previous = previous; 182 // #6109 - IE triggers two focus events and the second 183 // is asynchronous, so we need to reset the previous 184 // term synchronously and asynchronously :-( 185 setTimeout(function() { 186 self.previous = previous; 187 self.selectedItem = item; 188 }, 1); 189 } 190 191 if ( false !== self._trigger( "select", event, { item: item } ) ) { 192 self.element.val( item.value ); 193 } 194 // reset the term after the select event 195 // this allows custom select handling to work properly 196 self.term = self.element.val(); 197 198 self.close( event ); 199 self.selectedItem = item; 200 }, 201 blur: function( event, ui ) { 202 // don't set the value of the text field if it's already correct 203 // this prevents moving the cursor unnecessarily 204 if ( self.menu.element.is(":visible") && 205 ( self.element.val() !== self.term ) ) { 206 self.element.val( self.term ); 207 } 208 } 209 }) 210 .zIndex( this.element.zIndex() + 1 ) 211 // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 212 .css({ top: 0, left: 0 }) 213 .hide() 214 .data( "menu" ); 215 if ( $.fn.bgiframe ) { 216 this.menu.element.bgiframe(); 217 } 218 // turning off autocomplete prevents the browser from remembering the 219 // value when navigating through history, so we re-enable autocomplete 220 // if the page is unloaded before the widget is destroyed. #7790 221 self.beforeunloadHandler = function() { 222 self.element.removeAttr( "autocomplete" ); 223 }; 224 $( window ).bind( "beforeunload", self.beforeunloadHandler ); 225 }, 226 227 destroy: function() { 228 this.element 229 .removeClass( "ui-autocomplete-input" ) 230 .removeAttr( "autocomplete" ) 231 .removeAttr( "role" ) 232 .removeAttr( "aria-autocomplete" ) 233 .removeAttr( "aria-haspopup" ); 234 this.menu.element.remove(); 235 $( window ).unbind( "beforeunload", this.beforeunloadHandler ); 236 $.Widget.prototype.destroy.call( this ); 237 }, 238 239 _setOption: function( key, value ) { 240 $.Widget.prototype._setOption.apply( this, arguments ); 241 if ( key === "source" ) { 242 this._initSource(); 243 } 244 if ( key === "appendTo" ) { 245 this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] ) 246 } 247 if ( key === "disabled" && value && this.xhr ) { 248 this.xhr.abort(); 249 } 250 }, 251 252 _initSource: function() { 253 var self = this, 254 array, 255 url; 256 if ( $.isArray(this.options.source) ) { 257 array = this.options.source; 258 this.source = function( request, response ) { 259 response( $.ui.autocomplete.filter(array, request.term) ); 260 }; 261 } else if ( typeof this.options.source === "string" ) { 262 url = this.options.source; 263 this.source = function( request, response ) { 264 if ( self.xhr ) { 265 self.xhr.abort(); 266 } 267 self.xhr = $.ajax({ 268 url: url, 269 data: request, 270 dataType: "json", 271 context: { 272 autocompleteRequest: ++requestIndex 273 }, 274 success: function( data, status ) { 275 if ( this.autocompleteRequest === requestIndex ) { 276 response( data ); 277 } 278 }, 279 error: function() { 280 if ( this.autocompleteRequest === requestIndex ) { 281 response( [] ); 282 } 283 } 284 }); 285 }; 286 } else { 287 this.source = this.options.source; 288 } 289 }, 290 291 search: function( value, event ) { 292 value = value != null ? value : this.element.val(); 293 294 // always save the actual value, not the one passed as an argument 295 this.term = this.element.val(); 296 297 if ( value.length < this.options.minLength ) { 298 return this.close( event ); 299 } 300 301 clearTimeout( this.closing ); 302 if ( this._trigger( "search", event ) === false ) { 303 return; 304 } 305 306 return this._search( value ); 307 }, 308 309 _search: function( value ) { 310 this.pending++; 311 this.element.addClass( "ui-autocomplete-loading" ); 312 313 this.source( { term: value }, this.response ); 314 }, 315 316 _response: function( content ) { 317 if ( !this.options.disabled && content && content.length ) { 318 content = this._normalize( content ); 319 this._suggest( content ); 320 this._trigger( "open" ); 321 } else { 322 this.close(); 323 } 324 this.pending--; 325 if ( !this.pending ) { 326 this.element.removeClass( "ui-autocomplete-loading" ); 327 } 328 }, 329 330 close: function( event ) { 331 clearTimeout( this.closing ); 332 if ( this.menu.element.is(":visible") ) { 333 this.menu.element.hide(); 334 this.menu.deactivate(); 335 this._trigger( "close", event ); 336 } 337 }, 338 339 _change: function( event ) { 340 if ( this.previous !== this.element.val() ) { 341 this._trigger( "change", event, { item: this.selectedItem } ); 342 } 343 }, 344 345 _normalize: function( items ) { 346 // assume all items have the right format when the first item is complete 347 if ( items.length && items[0].label && items[0].value ) { 348 return items; 349 } 350 return $.map( items, function(item) { 351 if ( typeof item === "string" ) { 352 return { 353 label: item, 354 value: item 355 }; 356 } 357 return $.extend({ 358 label: item.label || item.value, 359 value: item.value || item.label 360 }, item ); 361 }); 362 }, 363 364 _suggest: function( items ) { 365 var ul = this.menu.element 366 .empty() 367 .zIndex( this.element.zIndex() + 1 ); 368 this._renderMenu( ul, items ); 369 // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate 370 this.menu.deactivate(); 371 this.menu.refresh(); 372 373 // size and position menu 374 ul.show(); 375 this._resizeMenu(); 376 ul.position( $.extend({ 377 of: this.element 378 }, this.options.position )); 379 380 if ( this.options.autoFocus ) { 381 this.menu.next( new $.Event("mouseover") ); 382 } 383 }, 384 385 _resizeMenu: function() { 386 var ul = this.menu.element; 387 ul.outerWidth( Math.max( 388 // Firefox wraps long text (possibly a rounding bug) 389 // so we add 1px to avoid the wrapping (#7513) 390 ul.width( "" ).outerWidth() + 1, 391 this.element.outerWidth() 392 ) ); 393 }, 394 395 _renderMenu: function( ul, items ) { 396 var self = this; 397 $.each( items, function( index, item ) { 398 self._renderItem( ul, item ); 399 }); 400 }, 401 402 _renderItem: function( ul, item) { 403 return $( "<li></li>" ) 404 .data( "item.autocomplete", item ) 405 .append( $( "<a></a>" ).text( item.label ) ) 406 .appendTo( ul ); 407 }, 408 409 _move: function( direction, event ) { 410 if ( !this.menu.element.is(":visible") ) { 411 this.search( null, event ); 412 return; 413 } 414 if ( this.menu.first() && /^previous/.test(direction) || 415 this.menu.last() && /^next/.test(direction) ) { 416 this.element.val( this.term ); 417 this.menu.deactivate(); 418 return; 419 } 420 this.menu[ direction ]( event ); 421 }, 422 423 widget: function() { 424 return this.menu.element; 425 } 426}); 427 428$.extend( $.ui.autocomplete, { 429 escapeRegex: function( value ) { 430 return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 431 }, 432 filter: function(array, term) { 433 var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); 434 return $.grep( array, function(value) { 435 return matcher.test( value.label || value.value || value ); 436 }); 437 } 438}); 439 440}( jQuery )); 441 442/* 443 * jQuery UI Menu (not officially released) 444 * 445 * This widget isn't yet finished and the API is subject to change. We plan to finish 446 * it for the next release. You're welcome to give it a try anyway and give us feedback,
447 * as long as you're okay with migrating your code later on. We can help with that, too. 448 * 449 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 450 * Dual licensed under the MIT or GPL Version 2 licenses. 451 * http://jquery.org/license 452 * 453 * http://docs.jquery.com/UI/Menu 454 * 455 * Depends: 456 * jquery.ui.core.js 457 * jquery.ui.widget.js 458 */ 459(function($) { 460 461$.widget("ui.menu", { 462 _create: function() { 463 var self = this; 464 this.element 465 .addClass("ui-menu ui-widget ui-widget-content ui-corner-all") 466 .attr({ 467 role: "listbox", 468 "aria-activedescendant": "ui-active-menuitem" 469 }) 470 .click(function( event ) { 471 if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) { 472 return; 473 } 474 // temporary 475 event.preventDefault(); 476 self.select( event ); 477 }); 478 this.refresh(); 479 }, 480 481 refresh: function() { 482 var self = this; 483 484 // don't refresh list items that are already adapted 485 var items = this.element.children("li:not(.ui-menu-item):has(a)") 486 .addClass("ui-menu-item") 487 .attr("role", "menuitem"); 488 489 items.children("a") 490 .addClass("ui-corner-all") 491 .attr("tabindex", -1) 492 // mouseenter doesn't work with event delegation 493 .mouseenter(function( event ) { 494 self.activate( event, $(this).parent() ); 495 }) 496 .mouseleave(function() { 497 self.deactivate(); 498 }); 499 }, 500 501 activate: function( event, item ) { 502 this.deactivate(); 503 if (this.hasScroll()) { 504 var offset = item.offset().top - this.element.offset().top, 505 scroll = this.element.scrollTop(), 506 elementHeight = this.element.height(); 507 if (offset < 0) { 508 this.element.scrollTop( scroll + offset); 509 } else if (offset >= elementHeight) { 510 this.element.scrollTop( scroll + offset - elementHeight + item.height()); 511 } 512 } 513 this.active = item.eq(0) 514 .children("a") 515 .addClass("ui-state-hover") 516 .attr("id", "ui-active-menuitem") 517 .end(); 518 this._trigger("focus", event, { item: item }); 519 }, 520 521 deactivate: function() { 522 if (!this.active) { return; } 523 524 this.active.children("a") 525 .removeClass("ui-state-hover") 526 .removeAttr("id"); 527 this._trigger("blur"); 528 this.active = null; 529 }, 530 531 next: function(event) { 532 this.move("next", ".ui-menu-item:first", event); 533 }, 534 535 previous: function(event) { 536 this.move("prev", ".ui-menu-item:last", event); 537 }, 538 539 first: function() { 540 return this.active && !this.active.prevAll(".ui-menu-item").length; 541 }, 542 543 last: function() { 544 return this.active && !this.active.nextAll(".ui-menu-item").length; 545 }, 546 547 move: function(direction, edge, event) { 548 if (!this.active) { 549 this.activate(event, this.element.children(edge)); 550 return; 551 } 552 var next = this.active[direction + "All"](".ui-menu-item").eq(0); 553 if (next.length) { 554 this.activate(event, next); 555 } else { 556 this.activate(event, this.element.children(edge)); 557 } 558 }, 559 560 // TODO merge with previousPage 561 nextPage: function(event) { 562 if (this.hasScroll()) { 563 // TODO merge with no-scroll-else 564 if (!this.active || this.last()) { 565 this.activate(event, this.element.children(".ui-menu-item:first")); 566 return; 567 } 568 var base = this.active.offset().top, 569 height = this.element.height(), 570 result = this.element.children(".ui-menu-item").filter(function() { 571 var close = $(this).offset().top - base - height + $(this).height(); 572 // TODO improve approximation 573 return close < 10 && close > -10; 574 }); 575 576 // TODO try to catch this earlier when scrollTop indicates the last page anyway 577 if (!result.length) { 578 result = this.element.children(".ui-menu-item:last"); 579 } 580 this.activate(event, result); 581 } else { 582 this.activate(event, this.element.children(".ui-menu-item") 583 .filter(!this.active || this.last() ? ":first" : ":last")); 584 } 585 }, 586 587 // TODO merge with nextPage 588 previousPage: function(event) { 589 if (this.hasScroll()) { 590 // TODO merge with no-scroll-else 591 if (!this.active || this.first()) { 592 this.activate(event, this.element.children(".ui-menu-item:last")); 593 return; 594 } 595 596 var base = this.active.offset().top, 597 height = this.element.height(); 598 result = this.element.children(".ui-menu-item").filter(function() { 599 var close = $(this).offset().top - base + height - $(this).height(); 600 // TODO improve approximation 601 return close < 10 && close > -10; 602 }); 603 604 // TODO try to catch this earlier when scrollTop indicates the last page anyway 605 if (!result.length) { 606 result = this.element.children(".ui-menu-item:first"); 607 } 608 this.activate(event, result); 609 } else { 610 this.activate(event, this.element.children(".ui-menu-item") 611 .filter(!this.active || this.first() ? ":last" : ":first")); 612 } 613 }, 614 615 hasScroll: function() { 616 return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]("scrollHeight"); 617 }, 618 619 select: function( event ) { 620 this._trigger("selected", event, { item: this.active }); 621 } 622}); 623 624}(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.