Blame | Last modification | View Log | RSS feed
/** jQuery UI Autocomplete 1.8.5** Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)* Dual licensed under the MIT or GPL Version 2 licenses.* http://jquery.org/license** http://docs.jquery.com/UI/Autocomplete** Depends:* jquery.ui.core.js* jquery.ui.widget.js* jquery.ui.position.js*/(function( $, undefined ) {$.widget( "ui.autocomplete", {options: {appendTo: "body",delay: 300,minLength: 1,position: {my: "left top",at: "left bottom",collision: "none"},source: null},_create: function() {var self = this,doc = this.element[ 0 ].ownerDocument;this.element.addClass( "ui-autocomplete-input" ).attr( "autocomplete", "off" )// TODO verify these actually work as intended.attr({role: "textbox","aria-autocomplete": "list","aria-haspopup": "true"}).bind( "keydown.autocomplete", function( event ) {if ( self.options.disabled ) {return;}var keyCode = $.ui.keyCode;switch( event.keyCode ) {case keyCode.PAGE_UP:self._move( "previousPage", event );break;case keyCode.PAGE_DOWN:self._move( "nextPage", event );break;case keyCode.UP:self._move( "previous", event );// prevent moving cursor to beginning of text field in some browsersevent.preventDefault();break;case keyCode.DOWN:self._move( "next", event );// prevent moving cursor to end of text field in some browsersevent.preventDefault();break;case keyCode.ENTER:case keyCode.NUMPAD_ENTER:// when menu is open or has focusif ( self.menu.element.is( ":visible" ) ) {event.preventDefault();}//passthrough - ENTER and TAB both select the current elementcase keyCode.TAB:if ( !self.menu.active ) {return;}self.menu.select( event );break;case keyCode.ESCAPE:self.element.val( self.term );self.close( event );break;default:// keypress is triggered before the input value is changedclearTimeout( self.searching );self.searching = setTimeout(function() {// only search if the value has changedif ( self.term != self.element.val() ) {self.selectedItem = null;self.search( null, event );}}, self.options.delay );break;}}).bind( "focus.autocomplete", function() {if ( self.options.disabled ) {return;}self.selectedItem = null;self.previous = self.element.val();}).bind( "blur.autocomplete", function( event ) {if ( self.options.disabled ) {return;}clearTimeout( self.searching );// clicks on the menu (or a button to trigger a search) will cause a blur eventself.closing = setTimeout(function() {self.close( event );self._change( event );}, 150 );});this._initSource();this.response = function() {return self._response.apply( self, arguments );};this.menu = $( "<ul></ul>" ).addClass( "ui-autocomplete" ).appendTo( $( this.options.appendTo || "body", doc )[0] )// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown).mousedown(function( event ) {// clicking on the scrollbar causes focus to shift to the body// but we can't detect a mouseup or a click immediately afterward// so we have to track the next mousedown and close the menu if// the user clicks somewhere outside of the autocompletevar menuElement = self.menu.element[ 0 ];if ( event.target === menuElement ) {setTimeout(function() {$( document ).one( 'mousedown', function( event ) {if ( event.target !== self.element[ 0 ] &&event.target !== menuElement &&!$.ui.contains( menuElement, event.target ) ) {self.close();}});}, 1 );}// use another timeout to make sure the blur-event-handler on the input was already triggeredsetTimeout(function() {clearTimeout( self.closing );}, 13);}).menu({focus: function( event, ui ) {var item = ui.item.data( "item.autocomplete" );if ( false !== self._trigger( "focus", null, { item: item } ) ) {// use value to match what will end up in the input, if it was a key eventif ( /^key/.test(event.originalEvent.type) ) {self.element.val( item.value );}}},selected: function( event, ui ) {var item = ui.item.data( "item.autocomplete" ),previous = self.previous;// only trigger when focus was lost (click on menu)if ( self.element[0] !== doc.activeElement ) {self.element.focus();self.previous = previous;}if ( false !== self._trigger( "select", event, { item: item } ) ) {self.term = item.value;self.element.val( item.value );}self.close( event );self.selectedItem = item;},blur: function( event, ui ) {// don't set the value of the text field if it's already correct// this prevents moving the cursor unnecessarilyif ( self.menu.element.is(":visible") &&( self.element.val() !== self.term ) ) {self.element.val( self.term );}}}).zIndex( this.element.zIndex() + 1 )// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781.css({ top: 0, left: 0 }).hide().data( "menu" );if ( $.fn.bgiframe ) {this.menu.element.bgiframe();}},destroy: function() {this.element.removeClass( "ui-autocomplete-input" ).removeAttr( "autocomplete" ).removeAttr( "role" ).removeAttr( "aria-autocomplete" ).removeAttr( "aria-haspopup" );this.menu.element.remove();$.Widget.prototype.destroy.call( this );},_setOption: function( key, value ) {$.Widget.prototype._setOption.apply( this, arguments );if ( key === "source" ) {this._initSource();}if ( key === "appendTo" ) {this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )}},_initSource: function() {var self = this,array,url;if ( $.isArray(this.options.source) ) {array = this.options.source;this.source = function( request, response ) {response( $.ui.autocomplete.filter(array, request.term) );};} else if ( typeof this.options.source === "string" ) {url = this.options.source;this.source = function( request, response ) {if (self.xhr) {self.xhr.abort();}self.xhr = $.getJSON( url, request, function( data, status, xhr ) {if ( xhr === self.xhr ) {response( data );}self.xhr = null;});};} else {this.source = this.options.source;}},search: function( value, event ) {value = value != null ? value : this.element.val();// always save the actual value, not the one passed as an argumentthis.term = this.element.val();if ( value.length < this.options.minLength ) {return this.close( event );}clearTimeout( this.closing );if ( this._trigger("search") === false ) {return;}return this._search( value );},_search: function( value ) {this.element.addClass( "ui-autocomplete-loading" );this.source( { term: value }, this.response );},_response: function( content ) {if ( content.length ) {content = this._normalize( content );this._suggest( content );this._trigger( "open" );} else {this.close();}this.element.removeClass( "ui-autocomplete-loading" );},close: function( event ) {clearTimeout( this.closing );if ( this.menu.element.is(":visible") ) {this._trigger( "close", event );this.menu.element.hide();this.menu.deactivate();}},_change: function( event ) {if ( this.previous !== this.element.val() ) {this._trigger( "change", event, { item: this.selectedItem } );}},_normalize: function( items ) {// assume all items have the right format when the first item is completeif ( items.length && items[0].label && items[0].value ) {return items;}return $.map( items, function(item) {if ( typeof item === "string" ) {return {label: item,value: item};}return $.extend({label: item.label || item.value,value: item.value || item.label}, item );});},_suggest: function( items ) {var ul = this.menu.element.empty().zIndex( this.element.zIndex() + 1 ),menuWidth,textWidth;this._renderMenu( ul, items );// TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivatethis.menu.deactivate();this.menu.refresh();this.menu.element.show().position( $.extend({of: this.element}, this.options.position ));menuWidth = ul.width( "" ).outerWidth();textWidth = this.element.outerWidth();ul.outerWidth( Math.max( menuWidth, textWidth ) );},_renderMenu: function( ul, items ) {var self = this;$.each( items, function( index, item ) {self._renderItem( ul, item );});},_renderItem: function( ul, item) {return $( "<li></li>" ).data( "item.autocomplete", item ).append( $( "<a></a>" ).text( item.label ) ).appendTo( ul );},_move: function( direction, event ) {if ( !this.menu.element.is(":visible") ) {this.search( null, event );return;}if ( this.menu.first() && /^previous/.test(direction) ||this.menu.last() && /^next/.test(direction) ) {this.element.val( this.term );this.menu.deactivate();return;}this.menu[ direction ]( event );},widget: function() {return this.menu.element;}});$.extend( $.ui.autocomplete, {escapeRegex: function( value ) {return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");},filter: function(array, term) {var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );return $.grep( array, function(value) {return matcher.test( value.label || value.value || value );});}});}( jQuery ));/** jQuery UI Menu (not officially released)** This widget isn't yet finished and the API is subject to change. We plan to finish* it for the next release. You're welcome to give it a try anyway and give us feedback,* as long as you're okay with migrating your code later on. We can help with that, too.** Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)* Dual licensed under the MIT or GPL Version 2 licenses.* http://jquery.org/license** http://docs.jquery.com/UI/Menu** Depends:* jquery.ui.core.js* jquery.ui.widget.js*/(function($) {$.widget("ui.menu", {_create: function() {var self = this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role: "listbox","aria-activedescendant": "ui-active-menuitem"}).click(function( event ) {if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {return;}// temporaryevent.preventDefault();self.select( event );});this.refresh();},refresh: function() {var self = this;// don't refresh list items that are already adaptedvar items = this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role", "menuitem");items.children("a").addClass("ui-corner-all").attr("tabindex", -1)// mouseenter doesn't work with event delegation.mouseenter(function( event ) {self.activate( event, $(this).parent() );}).mouseleave(function() {self.deactivate();});},activate: function( event, item ) {this.deactivate();if (this.hasScroll()) {var offset = item.offset().top - this.element.offset().top,scroll = this.element.attr("scrollTop"),elementHeight = this.element.height();if (offset < 0) {this.element.attr("scrollTop", scroll + offset);} else if (offset >= elementHeight) {this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());}}this.active = item.eq(0).children("a").addClass("ui-state-hover").attr("id", "ui-active-menuitem").end();this._trigger("focus", event, { item: item });},deactivate: function() {if (!this.active) { return; }this.active.children("a").removeClass("ui-state-hover").removeAttr("id");this._trigger("blur");this.active = null;},next: function(event) {this.move("next", ".ui-menu-item:first", event);},previous: function(event) {this.move("prev", ".ui-menu-item:last", event);},first: function() {return this.active && !this.active.prevAll(".ui-menu-item").length;},last: function() {return this.active && !this.active.nextAll(".ui-menu-item").length;},move: function(direction, edge, event) {if (!this.active) {this.activate(event, this.element.children(edge));return;}var next = this.active[direction + "All"](".ui-menu-item").eq(0);if (next.length) {this.activate(event, next);} else {this.activate(event, this.element.children(edge));}},// TODO merge with previousPagenextPage: function(event) {if (this.hasScroll()) {// TODO merge with no-scroll-elseif (!this.active || this.last()) {this.activate(event, this.element.children(":first"));return;}var base = this.active.offset().top,height = this.element.height(),result = this.element.children("li").filter(function() {var close = $(this).offset().top - base - height + $(this).height();// TODO improve approximationreturn close < 10 && close > -10;});// TODO try to catch this earlier when scrollTop indicates the last page anywayif (!result.length) {result = this.element.children(":last");}this.activate(event, result);} else {this.activate(event, this.element.children(!this.active || this.last() ? ":first" : ":last"));}},// TODO merge with nextPagepreviousPage: function(event) {if (this.hasScroll()) {// TODO merge with no-scroll-elseif (!this.active || this.first()) {this.activate(event, this.element.children(":last"));return;}var base = this.active.offset().top,height = this.element.height();result = this.element.children("li").filter(function() {var close = $(this).offset().top - base + height - $(this).height();// TODO improve approximationreturn close < 10 && close > -10;});// TODO try to catch this earlier when scrollTop indicates the last page anywayif (!result.length) {result = this.element.children(":first");}this.activate(event, result);} else {this.activate(event, this.element.children(!this.active || this.first() ? ":last" : ":first"));}},hasScroll: function() {return this.element.height() < this.element.attr("scrollHeight");},select: function( event ) {this._trigger("selected", event, { item: this.active });}});}(jQuery));