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