forked from GNUsocial/gnu-social
		
	
		
			
				
	
	
		
			608 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			608 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
|  * jQuery UI Autocomplete 1.8.10
 | |
|  *
 | |
|  * Copyright 2011, 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 ) {
 | |
| 
 | |
| // used to prevent race conditions with remote data sources
 | |
| var requestIndex = 0;
 | |
| 
 | |
| $.widget( "ui.autocomplete", {
 | |
| 	options: {
 | |
| 		appendTo: "body",
 | |
| 		delay: 300,
 | |
| 		minLength: 1,
 | |
| 		position: {
 | |
| 			my: "left top",
 | |
| 			at: "left bottom",
 | |
| 			collision: "none"
 | |
| 		},
 | |
| 		source: null
 | |
| 	},
 | |
| 
 | |
| 	pending: 0,
 | |
| 
 | |
| 	_create: function() {
 | |
| 		var self = this,
 | |
| 			doc = this.element[ 0 ].ownerDocument,
 | |
| 			suppressKeyPress;
 | |
| 
 | |
| 		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 || self.element.attr( "readonly" ) ) {
 | |
| 					return;
 | |
| 				}
 | |
| 
 | |
| 				suppressKeyPress = false;
 | |
| 				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 browsers
 | |
| 					event.preventDefault();
 | |
| 					break;
 | |
| 				case keyCode.DOWN:
 | |
| 					self._move( "next", event );
 | |
| 					// prevent moving cursor to end of text field in some browsers
 | |
| 					event.preventDefault();
 | |
| 					break;
 | |
| 				case keyCode.ENTER:
 | |
| 				case keyCode.NUMPAD_ENTER:
 | |
| 					// when menu is open and has focus
 | |
| 					if ( self.menu.active ) {
 | |
| 						// #6055 - Opera still allows the keypress to occur
 | |
| 						// which causes forms to submit
 | |
| 						suppressKeyPress = true;
 | |
| 						event.preventDefault();
 | |
| 					}
 | |
| 					//passthrough - ENTER and TAB both select the current element
 | |
| 				case 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 changed
 | |
| 					clearTimeout( self.searching );
 | |
| 					self.searching = setTimeout(function() {
 | |
| 						// only search if the value has changed
 | |
| 						if ( self.term != self.element.val() ) {
 | |
| 							self.selectedItem = null;
 | |
| 							self.search( null, event );
 | |
| 						}
 | |
| 					}, self.options.delay );
 | |
| 					break;
 | |
| 				}
 | |
| 			})
 | |
| 			.bind( "keypress.autocomplete", function( event ) {
 | |
| 				if ( suppressKeyPress ) {
 | |
| 					suppressKeyPress = false;
 | |
| 					event.preventDefault();
 | |
| 				}
 | |
| 			})
 | |
| 			.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 event
 | |
| 				self.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 autocomplete
 | |
| 				var menuElement = self.menu.element[ 0 ];
 | |
| 				if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
 | |
| 					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 triggered
 | |
| 				setTimeout(function() {
 | |
| 					clearTimeout( self.closing );
 | |
| 				}, 13);
 | |
| 			})
 | |
| 			.menu({
 | |
| 				focus: function( event, ui ) {
 | |
| 					var item = ui.item.data( "item.autocomplete" );
 | |
| 					if ( false !== self._trigger( "focus", event, { item: item } ) ) {
 | |
| 						// use value to match what will end up in the input, if it was a key event
 | |
| 						if ( /^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;
 | |
| 						// #6109 - IE triggers two focus events and the second
 | |
| 						// is asynchronous, so we need to reset the previous
 | |
| 						// term synchronously and asynchronously :-(
 | |
| 						setTimeout(function() {
 | |
| 							self.previous = previous;
 | |
| 							self.selectedItem = item;
 | |
| 						}, 1);
 | |
| 					}
 | |
| 
 | |
| 					if ( false !== self._trigger( "select", event, { item: item } ) ) {
 | |
| 						self.element.val( item.value );
 | |
| 					}
 | |
| 					// reset the term after the select event
 | |
| 					// this allows custom select handling to work properly
 | |
| 					self.term = self.element.val();
 | |
| 
 | |
| 					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 unnecessarily
 | |
| 					if ( 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] )
 | |
| 		}
 | |
| 		if ( key === "disabled" && value && this.xhr ) {
 | |
| 			this.xhr.abort();
 | |
| 		}
 | |
| 	},
 | |
| 
 | |
| 	_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 = $.ajax({
 | |
| 					url: url,
 | |
| 					data: request,
 | |
| 					dataType: "json",
 | |
| 					autocompleteRequest: ++requestIndex,
 | |
| 					success: function( data, status ) {
 | |
| 						if ( this.autocompleteRequest === requestIndex ) {
 | |
| 							response( data );
 | |
| 						}
 | |
| 					},
 | |
| 					error: function() {
 | |
| 						if ( this.autocompleteRequest === requestIndex ) {
 | |
| 							response( [] );
 | |
| 						}
 | |
| 					}
 | |
| 				});
 | |
| 			};
 | |
| 		} 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 argument
 | |
| 		this.term = this.element.val();
 | |
| 
 | |
| 		if ( value.length < this.options.minLength ) {
 | |
| 			return this.close( event );
 | |
| 		}
 | |
| 
 | |
| 		clearTimeout( this.closing );
 | |
| 		if ( this._trigger( "search", event ) === false ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		return this._search( value );
 | |
| 	},
 | |
| 
 | |
| 	_search: function( value ) {
 | |
| 		this.pending++;
 | |
| 		this.element.addClass( "ui-autocomplete-loading" );
 | |
| 
 | |
| 		this.source( { term: value }, this.response );
 | |
| 	},
 | |
| 
 | |
| 	_response: function( content ) {
 | |
| 		if ( !this.options.disabled && content && content.length ) {
 | |
| 			content = this._normalize( content );
 | |
| 			this._suggest( content );
 | |
| 			this._trigger( "open" );
 | |
| 		} else {
 | |
| 			this.close();
 | |
| 		}
 | |
| 		this.pending--;
 | |
| 		if ( !this.pending ) {
 | |
| 			this.element.removeClass( "ui-autocomplete-loading" );
 | |
| 		}
 | |
| 	},
 | |
| 
 | |
| 	close: function( event ) {
 | |
| 		clearTimeout( this.closing );
 | |
| 		if ( this.menu.element.is(":visible") ) {
 | |
| 			this.menu.element.hide();
 | |
| 			this.menu.deactivate();
 | |
| 			this._trigger( "close", event );
 | |
| 		}
 | |
| 	},
 | |
| 	
 | |
| 	_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 complete
 | |
| 		if ( 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 );
 | |
| 		this._renderMenu( ul, items );
 | |
| 		// TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
 | |
| 		this.menu.deactivate();
 | |
| 		this.menu.refresh();
 | |
| 
 | |
| 		// size and position menu
 | |
| 		ul.show();
 | |
| 		this._resizeMenu();
 | |
| 		ul.position( $.extend({
 | |
| 			of: this.element
 | |
| 		}, this.options.position ));
 | |
| 	},
 | |
| 
 | |
| 	_resizeMenu: function() {
 | |
| 		var ul = this.menu.element;
 | |
| 		ul.outerWidth( Math.max(
 | |
| 			ul.width( "" ).outerWidth(),
 | |
| 			this.element.outerWidth()
 | |
| 		) );
 | |
| 	},
 | |
| 
 | |
| 	_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;
 | |
| 				}
 | |
| 				// temporary
 | |
| 				event.preventDefault();
 | |
| 				self.select( event );
 | |
| 			});
 | |
| 		this.refresh();
 | |
| 	},
 | |
| 	
 | |
| 	refresh: function() {
 | |
| 		var self = this;
 | |
| 
 | |
| 		// don't refresh list items that are already adapted
 | |
| 		var 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 previousPage
 | |
| 	nextPage: function(event) {
 | |
| 		if (this.hasScroll()) {
 | |
| 			// TODO merge with no-scroll-else
 | |
| 			if (!this.active || this.last()) {
 | |
| 				this.activate(event, this.element.children(".ui-menu-item:first"));
 | |
| 				return;
 | |
| 			}
 | |
| 			var base = this.active.offset().top,
 | |
| 				height = this.element.height(),
 | |
| 				result = this.element.children(".ui-menu-item").filter(function() {
 | |
| 					var close = $(this).offset().top - base - height + $(this).height();
 | |
| 					// TODO improve approximation
 | |
| 					return close < 10 && close > -10;
 | |
| 				});
 | |
| 
 | |
| 			// TODO try to catch this earlier when scrollTop indicates the last page anyway
 | |
| 			if (!result.length) {
 | |
| 				result = this.element.children(".ui-menu-item:last");
 | |
| 			}
 | |
| 			this.activate(event, result);
 | |
| 		} else {
 | |
| 			this.activate(event, this.element.children(".ui-menu-item")
 | |
| 				.filter(!this.active || this.last() ? ":first" : ":last"));
 | |
| 		}
 | |
| 	},
 | |
| 
 | |
| 	// TODO merge with nextPage
 | |
| 	previousPage: function(event) {
 | |
| 		if (this.hasScroll()) {
 | |
| 			// TODO merge with no-scroll-else
 | |
| 			if (!this.active || this.first()) {
 | |
| 				this.activate(event, this.element.children(".ui-menu-item:last"));
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			var base = this.active.offset().top,
 | |
| 				height = this.element.height();
 | |
| 				result = this.element.children(".ui-menu-item").filter(function() {
 | |
| 					var close = $(this).offset().top - base + height - $(this).height();
 | |
| 					// TODO improve approximation
 | |
| 					return close < 10 && close > -10;
 | |
| 				});
 | |
| 
 | |
| 			// TODO try to catch this earlier when scrollTop indicates the last page anyway
 | |
| 			if (!result.length) {
 | |
| 				result = this.element.children(".ui-menu-item:first");
 | |
| 			}
 | |
| 			this.activate(event, result);
 | |
| 		} else {
 | |
| 			this.activate(event, this.element.children(".ui-menu-item")
 | |
| 				.filter(!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));
 |