/* Jquery.ui.combobox, planio custom development */
/* Version 1.1 */
/*
 * this section if a fork of jQuery UI combobox 1.8.1 for Planio
 *
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/combobox
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *
 * Note:
 * 	-remove dependancy to outerwidth()/width() -> unreliable if the element is hidden, different browser = diffenrent value
 * 	-Do not call the change function on initialisation (if (!event || event.type != 'initDefault')) -> causes problem for planio, put an option instead
 * 
 */
(function( $ ) {

$.widget( "ui.combobox", {
	options: {
		minLength: 1,
		delay: 300,
		mode:"input", // input || button
		buttonText: "",
		button: "",
		select: "single", // single || multiple
		open: function() {},
		close: function() {},
		change: function() {},
		cancel: function() {},
		openOnFocus:true,
		alternateInputValue: function() {},
		focus: function() {},
		noCheckAll:false, // if this option is set to true, when all items are checked the list will display none of them to be checked.
		data:{},
		width:false,
		map: {label:"label",value:"value", icon:"icon"},
		selectIfOne: true,
		cacheRender:false,
		preRender:false,
		fx:true,
		allowNewValue:false
	},
	_create: function() {
		var self = this;
		self.selection = [];
		self.lastResponse = [];

		if (self.options.mode == "button")
			self.container = $("<div/>").addClass("ui-widget ui-combobox-button").css({width:self.options.width || 175});
		else if (self.options.mode == "input")
			self.container = $("<div/>").addClass("ui-widget ui-widget-content ui-combobox ui-corner-right").css({width:self.options.width || self.element.width()});

		self.element.wrap(self.container.attr("id","ui-combobox-container-" + Math.floor(Math.random()*10000)));
		self.container = self.element.parent();

		if (typeof self.options.button == "string" && self.options.button == "") {
			self.dropButton = $("<button>&nbsp;</button>")
			.attr("tabIndex", -1)
			.attr("title", "Show All Items")
			.insertAfter(self.element)
			if (self.options.mode == "button") {
				self.dropButton
					.button({
					icons: {
						primary: "ui-icon-wrench"
						,secondary: "ui-icon-triangle-1-s"
					}
					,text: true
					,label:self.options.buttonText})
					.addClass("ui-button-icon");		
			}
		} else {
			self.dropButton = $(self.options.button).appendTo(self.container);
		}		

		if (self.options.mode == "input") {
			self.dropButton
				.button({
				icons: {
					primary: "ui-icon-triangle-1-s"
				}
				,text: false})
				.removeClass("ui-corner-all")		
				.addClass("ui-button-icon ui-combobox-dropdown-button")
		}

		self.dropButton.click(function( e ) {
			e.preventDefault();
			// close if already visible
			if (self.menuContainer.is(":visible")) {
				self.confirmSelection();
				return;
			}
			// pass empty string as value to search for
			self.search("");
			self.element.focus();
		}).mousedown(function( event ) {
			if (self.menuContainer.is(":visible")) {
				event.preventDefault();
			};
		});

		// Determining wether we were given a <select> or actual data.
		self._buildDataIfSelect();

		if (self.element.is("input[type='text']"))
			self.input = self.element;
		else {
			self.element.hide();
			self.element = $("<input/>")
			.insertAfter(self.element);
		}
		//dropButton.outerWidth() is unreliable
		var dropButtonWidth = parseInt(self.dropButton.css('width')) + 
							  parseInt(self.dropButton.css('padding-left')) +
							  parseInt(self.dropButton.css('padding-right')) +
							  parseInt(self.dropButton.css('border-left-width')) + 
							  parseInt(self.dropButton.css('border-right-width'));

		self.element.addClass("ui-widget ui-widget-content").css({width: self.options.width - dropButtonWidth - 1 });
		self.options.source = function(request, response) {
			var matcher = new RegExp(request.term, "i");
			self.readData(request.term);
			response($.map(self.data, function(item, index) {
		
				if ((!request.term || matcher.test(item.label))) {
			//		item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.combobox.escapeRegex(request.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>")
					return item;
				}
			}));
		
		}

		self.element
			.addClass( "ui-combobox-input" )
			.bind( "keypress.combobox", function( event ) {
				var keyCode = $.ui.keyCode;
				switch( event.keyCode ) {
				case keyCode.PAGE_UP:
					self.previousPage( event );
					break;
				case keyCode.PAGE_DOWN:
					self.nextPage( event );
					break;
				case keyCode.UP:
					self.previous( event );
					// prevent moving cursor to beginning of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.DOWN:
					self.next( event );
					// prevent moving cursor to end of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.TAB:
				case keyCode.ENTER:
					if (self.options.select == "multiple")
						self.menuContainer.find(".okButton").click();
					else
						self.menuContainer.find("a.ui-state-hover").click();
					break;
				case keyCode.ESCAPE:
					event.stopPropagation();
					self.cancel(event)
					break;
				case keyCode.SPACE:
					if (self.menu.find("a.ui-state-hover").length > 0){
						//If an element is hovered select it, if none are, let the space be appended to the input.
						event.preventDefault();
						self._toggleselect(event, {item:self.menu.find("a.ui-state-hover") });
					}

					if (self.newTypingDisabled) {
						event.preventDefault();
						return;
					}

				case keyCode.LEFT:
				case keyCode.RIGHT:
				case keyCode.SHIFT:
				case keyCode.CONTROL:
				case keyCode.ALT:
					// ignore metakeys (shift, ctrl, alt)
					break;
				default:
					// keypress is triggered before the input value is changed
					if (self.newTypingDisabled || true) {
//						event.preventDefault();
//						return;
					}

					clearTimeout( self.searching );
					self.searching = setTimeout(function() {
						self.search( null, event );
					}, self.options.delay );
					break;
				}
			})
			.bind( "focus.combobox", function(event) {
				self.options.focus.apply(self.element, [event, self]); // Fire the callback.
				self.dropButton.addClass("ui-state-focus")
				self.element.data("focus",true);
				
				if (self.options.allowNewValue===true) {
					self.selection = self.tentativeSelection = {items:[], elements:$()};
					self.element.val("");
				}

				if (self.options.openOnFocus) {
					// pass empty string as value to search for, displaying all results
					if (self.menu.is(":visible"))
						return true;
					self.search("");
				}

			})
			.bind( "blur.combobox", function( event, bypass ) {
				if (self.element.data("focus") === false) //Prevent the blur from being called twice since code below is poorly written!
					return;
				self.element.data("focus",false);
				// IE hack. Because mousedown.preventDefault will not prevent the loss of focus like in FF/Webkit
				if (self.element.data("mousedown") == true) {
					self.element.removeData("mousedown");
					event.preventDefault();
					self.element.focus();
					return false;
				}
				if (!bypass)
					self.confirmSelection(event);

			}).bind( "click.combobox", function ( event ) {
				if (self.menuContainer.is(":hidden"))
					self.search("");
			});

		if (self.options.mode == "button")
			self.element.hide();

		self.response = function() {
			return self._response.apply( self, arguments );
		};

		self.confirmSelection = function(event) { 
			var self = this;
			
			if (self.options.allowNewValue == true && self.tentativeSelection.items.length ==0) {
				//Create a new value and set it
				
				var selection = {items:[{label:self.element.val(), value:self.element.val()}],elements:$()};
				return self.itemSelected(event, {selection:selection});				
			}
			
			self._updateDisplayFromItems(self.selection);
			self.dropButton.removeClass("ui-state-focus")
	
			if (self.options.select == "multiple")
				return self.menuContainer.find(".okButton").click();
			else
				self.close();
	
		}		
		self.menuContainer = $( "<div></div>" )
			.addClass( "ui-combobox-menu-container ui-widget")
			.appendTo( self.element.parent() )
			.hide()
			// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
			.css({ top: 0, left: 0 })
			// workaround a mousedown in the menu to trigger the .blur() of the input.
			.mousedown(function( event ) {
				event.preventDefault();
				self.element.data("mousedown",true); //IE hack, the above preventDefault() will not prevent IE from removing the focus on the input field!! but this option will
			})
			.mouseup(function( event ) {
				self.element.removeData("mousedown");
			});			
		
		if (self.options.select == "multiple") {
			self.menuHeading = $( "<div></div>" )
				.addClass( "ui-combobox-menu-heading ui-widget-content" )
				.appendTo(self.menuContainer)

			$( "<div>Select All</div>" ).addClass("fake_link")
				.appendTo(self.menuHeading)
				.click(function( e ) {
					self.menuContainer.find(".ui-combobox-menu-item-checkbox:not(:checked)").closest("li").each(function() { self._toggleselect(e, {item:$(this)}) });
				})

			$( "<div>Clear</div>" ).addClass("fake_link")
				.appendTo(self.menuHeading)
				.click(function() {
					self.deselectall();
				});

			// Clear so the heading get the proper height.
			$( "<div></div>" ).addClass("clear")
				.appendTo(self.menuHeading)

		}

		self.menu = $( "<ul></ul>" )
			.click(function( event ) {
				if ( !$( event.target ).closest( ".ui-combobox-menu-item a" ).length || $( event.target ).closest( "li" ).hasClass("disabled") ) {
					return;
				}
				// temporary
				if (! $(event.target).is("input")) 
					event.preventDefault();
				self._toggleselect( event, { item: self.menu.find("a.ui-state-hover") } );
			})
			.addClass( "ui-combobox-menu ui-widget ui-widget-content" )
			.appendTo( self.menuContainer )
			.zIndex( self.element.zIndex() + 1 )
			.delegate("a", "mouseover mouseout", function(event) {
				// mouseenter doesn't work with event delegation
				if (event.type=="mouseover")
					self.activate( event, $(this).parent() );
				if (event.type=="mouseout")
					self.deactivate();
			});

		if ( $.fn.bgiframe ) {
			 self.menu.bgiframe();
		}

		if (self.options.select == "multiple") { //Also add a OK / CANCEL button
			self.menu;
			var butContainer = $("<div></div>").addClass("ui-combobox-menu-item ui-combobox-menu-item-buttons-container ui-corner-bottom ui-widget-content");
			var buttonOk, buttonCancel;
			
			//First button, the OK button
			buttonOk = $("<button>").button({
				icons: {
					primary: "ui-icon-check"
				},
				label: "Ok"
			}).click(function(event) {
				self.selection = self.tentativeSelection;
				self._updateDisplayFromItems(self.selection);
				self.options.change.apply(self.element, [event, self]); // Fire the callback only if the element is visible..
				self.close(event);
			}).addClass("okButton")
			  .appendTo(butContainer);

			//Second button, the CANCEL button
			buttonCancel = $("<button>").button({
				icons: {
					primary: "ui-icon-cancel"
				},
				label: "Cancel"
			}).click(function() {
				self.cancel();
			}).appendTo(butContainer);

			butContainer.appendTo(self.menuContainer);
		}

		//Call reset to make sure the default value are selected.
		self.resetToDefault({type:"initDefault", render:self.options.preRender});
	}, //end of create.

	selectall: function() {
		var self = this;
		if (self.menu.is(":visible"))
			self.toggleselect( {type:"selectAll"}, {item:self.menu.find("a")} );
		self.options.source({term:""}, function(dataSet) {
			self.selection = self.tentativeSelection = self.lastResponse = {elements:$(""), items: dataSet};
			// selection is changing, better fire the callback!
			self._updateDisplayFromItems(self.selection);
			self.options.change.apply(self.element, [{type:"selectall"}, self]); // Fire the callback 
		})
	},
	
	deselectall: function() {
		var self = this;
		self.toggleselect( {type:"deselectAll"}, {item:self.menu.find("a")} );
		var selection = {items: [], elements: $("")};
		self.selection = self.tentativeSelection = selection;
		self._updateDisplayFromItems(self.selection);
	},
	

	// Reset restore original default values.
	resetToDefault: function(event) {
		var self=this;
		// Getting the default values by querying the source 
		self._buildDataIfSelect() // Re-read the source if it's a select.

		var selection = { items: [], elements: $("") }
		self.options.source({term:""}, function(dataSet) {
			self.lastResponse = dataSet;
			$.each(dataSet, function(index, record) {
				if (record.selected && record.selected==true)	{
					selection.items.push(record);
				}			
			});
		})

		self.selection = self.tentativeSelection = selection;
		self._updateDisplayFromItems(self.selection);

		if (event && event.render)
			self._suggest( self.lastResponse,false, true );
		if (!event || event.type != 'initDefault') // Fire the callback, but not on the first reset
			self.options.change.apply(self.element, [{type:"reset"}, self]); 	
	},

	destroy: function() {
		var self=this;
		self.element
			.removeClass( "ui-combobox-input" )
			.unbind(".combobox");
		self.menu.remove();
		$.Widget.prototype.destroy.call( self );
	},

	_setOption: function( key ) {
		var self=this;
		$.Widget.prototype._setOption.apply( self, arguments );
		if ( key === "source" ) {
			// TODO stuff to init source
		}
	},

	search: function( value, event ) {
		var self=this;
		value = value != null ? value : self.element.val();

		return self._search( value );
	},

	_search: function( value ) {
		var self=this;
		self.term = self.element
			// always save the actual value, not the one passed as an argument
			.val();


		self.options.source( { term: value }, self.response );
	},

	_response: function( content ) {
		var self=this;
		if (content.length==0 && self.options.allowNewValue == true) {
			self.menuContainer.slideUp(100);
		} else if ( content.length>0 || self.options.allowNewValue == true ) {
			self._suggest( content,true );
			self.lastResponse = content;
		} else {
			self.close();
		}
	},

	cancel: function( event ) {
		var self = this;
		var event = event || {};
		event.type="cancel";
		self.tentativeSelection = self.selection; // restore the last valid selection
		self.options.cancel(event, self);
		self.close( event );
	}, 
	
	close: function( event ) {
		var self=this;
		clearTimeout( self.closing );

		if (self.options.mode == "button")
			$("body").unbind("mousedown." + self.container.attr("id"));
		
		if ( self.menu.is(":visible")) {
			self.menuContainer.slideUp(100, function() {
				self.deactivate();
				// Fire the callback
				self.options.close.apply(self.element, [event, self]);		
			});
		}

		// Changing the display value.
		self._updateDisplayFromItems(self.selection);

		// Blur
		self.element.trigger("blur",[true]);
	},
	_updateDisplayFromItems: function(selection) {
		var self = this;

		//Ask a user-provided callback for the value
		var value = self.options.alternateInputValue.apply(self.element, [{}, self]);

		// Nothing provided, no problemo I have a default case for that!!
		if (typeof value != "string")
			value = $.map(selection.items, function(item, index) { return $("<div>" + item.label + "</div>").text(); }).join(", ")

		self.element.val(value);

		//Does my input already have an icon?
		if (self.attachedIcon) {
			self.attachedIcon.remove();
			delete self.attachedIcon;
			self.element.removeClass("withIcon").width(self.element.width()+16);
		}
		
		if (selection.items.length == 1 && selection.items[0].icon) // That single selection had an icon, better to show it!
		{	
			var icon = $("<img>")
				.attr("src",selection.items[0].icon).attr("alt","")
				.addClass("ui-combobox-selected-icon")
				.insertBefore(self.element)
				.position( { of: self.element, my: "left center", at:"left center", offset: "5 0" })
			self.attachedIcon = icon;
			self.element.addClass("withIcon").width(self.element.width()-16);
		}
	},	

	setLabel: function(val) {
		var self = this;
		if ($.isArray(val))
			return self._updateDisplayFromItems(val);
		else if (typeof val == "string")
			return self._updateDisplayFromItems({items:[{label:val}]});
		else if (typeof val == "object")
			return self._updateDisplayFromItems({items:[val]});
	}, 

	isValid: function() {
		var self = this;
		var value = self.element.val();
		var arrayOfValues = $.map(self.readData(value), function(item) { return $("<div>" + item.label + "</div>").text(); });
		return ($.inArray(value, arrayOfValues) >= 0);
	},


	_suggest: function( items, openAfterRender, forceRender ) {
		forceRender = forceRender || false;

		var self = this;
		var ul = self.menu;
		if (self.options.cacheRender && ul.find("li").length > 0 && !(forceRender)) {
			// The user asked the menu to be cached, this means the source should not be re-read everytime the user click the dropdown.
		}
		else {
			var newUl = $("<ul></ul>");
			self._renderMenu( newUl, items );
			
			ul.detach();
			ul.empty();
			newUl.children().appendTo(ul);
			if (self.options.select == "multiple")
				ul.insertAfter(self.menuContainer.children("div.ui-combobox-menu-heading"));
			else
				ul.appendTo(self.menuContainer);

		}

		if (openAfterRender)
			self.open();
	},

	open: function() {
		var self = this;

		//Reset the tentativeSelection to the current selection
		self.tentativeSelection = self.selection;
		
		// Already opened, no need to work
		if (self.menuContainer.is(":visible"))
			return;

		// This function will be called until the menu is done being animated.
		var checkForAnimation = function() {
				if (self.element.parent().is(":animated")) { // The dropdown box is being animated.. We should wait it's final position before setting the menu's one.
					window.setTimeout(function() { checkForAnimation(); }, 100);
				}
				else {
					if (self.options.fx)
						self.menuContainer.css({"zIndex":100,"width":""}).slideDown(100)
					else
						self.menuContainer.css({"zIndex":100,"width":""}).show()
						
					self.menuContainer.position({
						my: "left top",
						at: "left bottom",
						of: self.container,
						collision: "none",
						offset:"1 0"
					});

					inputWidth = self.container.innerWidth();
					if (self.menuContainer.width()+25 < inputWidth)
						self.menuContainer.width(inputWidth-5);
					else 
						self.menuContainer.width(self.menuContainer.width()+25);
					
					//Firing the callback for open!
					if (self.options.mode == "button") { // The menu is opened and we are in button mode (no input field). We need to set-up a way of closing this menu if click is done outside of it.
						$("body").bind("mousedown." + self.container.attr("id"), function( e ) {
							if ($(e.target).closest("div.ui-combobox-button").attr("id") != self.container.attr("id")) {
								self.confirmSelection(e);				
							}
						})
					}
					self.options.open.apply(self.element, [{type:"open"}, self]); // Fire the callback

				}
		}

		checkForAnimation();	

	}, _buildDataIfSelect: function() {
		var self = this;
		if (self.element.is("select")) {
			self.data = [];
			self.element.children("option").each(function() {
				var row = {
					label: $(this).text(),
					value: $(this).val(),
					disabled: ($(this).attr("disabled")),
					icon: $(this).attr("data-icon"),
					selected: ($(this).attr("selected"))
				}				
				$(this).data("item.combobox",row);
				self.data.push(row);
			});
		}
	
	}, _renderMenu: function( ul, items ) {
		var self = this;
		$.each( items, function( index, item ) {
			self._renderItem( ul, item, items );
		});
	},

	_renderItem: function( ul, item, items) {
		var self = this;
		var addon = $("<span />"), icon, checkbox, addonNeeded=false; //Addon is the span that will contain icon and checkbox if any.

		// Link inside the li
		var a = $("<a>" + item.label + "</a>");

		var itemLi =  $( "<li></li>" )
			.data( "item.combobox", item )
			.append( a )
			.appendTo( ul )
			.toggleClass("disabled", (item.disabled==true))
			.addClass("ui-combobox-menu-item")

		if (self.options.select == "multiple" && item.disabled != true) {
			checkbox = $("<input type='checkbox' class='ui-combobox-menu-item-checkbox' />");
			addon.append(checkbox)
			addonNeeded = true;
		}

		if (item.icon) {
			icon = $("<img>").attr("src", item.icon).attr("alt","");
			addon.append(icon)
			addonNeeded = true;
		}

		if (item.icon && self.options.select == "multiple") {
			icon.css({marginLeft:0});
		}

		if (item.css) {
			a.css(item.css);
		}

		if (item.css_class)
			a.addClass(item.css_class);

		if (addonNeeded)
			a.prepend(addon);

		if (self.options.select == "multiple") {
			var selectedItems = self.selection.items;
			var selectedValues = $.map(selectedItems, function(i,v) { return i.value; });
			if ($.inArray(item.value, selectedValues)>=0 && ((self.options.noCheckAll && selectedItems.length) != items.length || selectedItems.length == 1)) {
				checkbox.attr("checked","checked");
				a.addClass("ui-combobox-menu-item-selected");
			}		
		}

	},


	readData: function(s) {
		var self = this;
		var data;
		
		if (self.data && !$.isFunction(self.options.data))
			return self.data;
		
		if ($.isFunction(self.options.data))
			data = self.options.data(s);
		else
			data = self.options.data
		
		var newData = [];
		$.each(data, function(i,row) {
			//Normalize data row.
			newData.push(self.normalizeRow(row))
		});

		if (self.options.selectIfOne == true && newData.length == 1)
			newData[0].selected = true;

		self.data = newData;
		return self.data;
	}, 

	normalizeRow: function(row) {
		var self = this;
		if (typeof row == "number")
			row = row.toString();
		if (typeof row == "string") {
			row = {label:row, value:row};
		}
		if (typeof row !== "object")
			return;

		row.disabled = row.disabled || false;

		var map = self.options.map;
		row.label = row[map.label] || row.label || row;
		row.value = row[map.value] || row.value || row.label;
		row.icon = row[map.icon];

		return row;		
	},

	widget: function() {
		var self=this;
		return self.menu;
	}, 

	_select: function( event, ui ) { //ui.item is the <a>
		var self=this;
		if (!ui.item)
			return false;

		ui.item.addClass("ui-combobox-menu-item-selected");
		if (self.options.select == "multiple") {
			ui.item.find(".ui-combobox-menu-item-checkbox").attr("checked","checked");
		} else {
			var selection = {};
			selection.elements = ui.item;
			selection.items = [ui.item.parent().data("item.combobox")];
			self.itemSelected(event, $.extend(ui, {selection: selection}));		
		}
	},

	_deselect: function( event, ui ) {
		// Can be called only in the context of a multiple.
		var self=this;
		if (!ui.item)
			return false;
		if (self.options.select == "multiple") {
			ui.item.removeClass("ui-combobox-menu-item-selected")
				.find(".ui-combobox-menu-item-checkbox").removeAttr("checked");
		} else {
			self.close( event );		
		}
	},

	toggleselect: function( event, ui ) {
		var self=this;
		self._toggleselect(event, ui);
	}, 

	_toggleselect: function( event, ui ) {
		var self=this;
		if (!ui.item)
			return false;
		if (ui.item.is("li"))
			ui.item = ui.item.children("a");


		if (self.options.select == "single") //Reset the selection
			self.menu.find(".ui-combobox-menu-item-selected").removeClass("ui-combobox-menu-item-selected");

		// TODO just make sure this function, _select and _deselect work with multiple items
		if (self.options.select == "multiple" && (event.type == "selectAll" || event.type == "deselectAll")) {
			if (event.type == "selectAll") {
				$.each(ui.item, function(index,item) {
					var item = $(item)
					if (!item.hasClass("ui-combobox-menu-item-selected"))
						self._select(event, {item:item});
				})			
			}
			if (event.type == "deselectAll") {
				$.each(ui.item, function(index,item) {
					var item = $(item)
					if (item.hasClass("ui-combobox-menu-item-selected"))
						self._deselect(event, {item:item});
				})			
			}
		} else {
			if (ui.item.hasClass("ui-combobox-menu-item-selected"))
				self._deselect(event, ui)
			else 
				self._select(event, ui)
		}

		var selection = {items: []};
		selection.elements = self.menu.find(".ui-combobox-menu-item-selected");
		selection.elements.parent().each(function(i,e) { selection.items.push($(e).data("item.combobox")); })
		
		self.selectionChanging(event, $.extend(ui, {selection: selection}));
	},

	hasScroll: function() {
		var self=this;
		return self.menu.height() < self.menu.attr("scrollHeight");
	},

	activate: function( event, item ) {
		// Called when an item is hovered
		var self=this;
		self.deactivate();
		if (self.hasScroll()) {
			var offset = item.offset().top - self.menu.offset().top,
				scroll = self.menu.attr("scrollTop"),
				elementHeight = self.menu.height();
			if (offset < 0) {
				self.menu.attr("scrollTop", scroll + offset);
			} else if (offset > elementHeight) {
				self.menu.attr("scrollTop", scroll + offset - elementHeight + item.height());
			}
		}
		if (item.hasClass("disabled"))
			return false;
			
		self.active = item.eq(0)
			.children("a")
				.addClass("ui-state-hover")
				.attr("id", "ui-active-menuitem")
			.end();
	},

	deactivate: function( event, item ) {
		var self=this;
		if (!self.active) { return; }

		self.active.children("a")
			.removeClass("ui-state-hover")
			.removeAttr("id");
		self._trigger("blur");
		self.active = null;
	},

	selectionChanging: function ( event, ui ) {
		var self = this;
		//This one will be called when options.select=="multiple"
		self.tentativeSelection = ui.selection;
		if (self.tentativeSelection.elements.length > 1)
			self.newTypingDisabled = true;
		else
			self.newTypingDisabled = false;

		self._updateDisplayFromItems(self.tentativeSelection);					
	},
	itemSelected: function( event, ui ) { 
		var self = this;
		// This one will be called when options.select==single
		//alert(ui.selection.items[0]);
		self.tentativeSelection = self.selection = ui.selection;
		self._updateDisplayFromItems(self.selection);
		self.options.change.apply(self.element, [event, self]); // Fire the callback.
		self.close(event);
	},

	next: function(event) {
		var self=this;
		self.move("next", ".ui-combobox-menu-item:first", event);
	},

	previous: function(event) {
		var self=this;
		self.move("prev", ".ui-combobox-menu-item:last", event);
	},

	first: function() {
		var self=this;
		return self.active && !self.active.prev().length;
	},

	last: function() {
		var self=this;
		return self.active && !self.active.next().length;
	},

	move: function(direction, edge, event) {
		var self=this;
		self.open()
		if (!self.active) {
			self.activate(event, self.menu.children(edge));
			return;
		}
		var next = self.active[direction + "All"](".ui-combobox-menu-item:not(.disabled)").eq(0);
		if (next.length) {
			self.activate(event, next);
		} else {
			self.activate(event, self.menu.children(edge));
		}
	},

	// TODO merge with previousPage
	nextPage: function(event) {
		var self=this;
		if (self.hasScroll()) {
			// TODO merge with no-scroll-else
			if (!self.active || self.last()) {
				self.activate(event, self.menu.children(":first"));
				return;
			}
			var base = self.active.offset().top,
				height = self.menu.height(),
				result = self.menu.children("li").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 = self.menu.children(":last");
			}
			self.activate(event, result);
		} else {
			self.activate(event, self.menu.children(!self.active || self.last() ? ":first" : ":last"));
		}
	},

	// TODO merge with nextPage
	previousPage: function(event) {
		var self=this;
		if (self.hasScroll()) {
			// TODO merge with no-scroll-else
			if (!self.active || self.first()) {
				self.activate(event, self.menu.children(":last"));
				return;
			}

			var base = self.active.offset().top,
				height = self.menu.height();
				result = self.menu.children("li").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 = self.menu.children(":first");
			}
			self.activate(event, result);
		} else {
			self.activate(event, self.menu.children(!self.active || self.first() ? ":last" : ":first"));
		}
	}


});

$.extend( $.ui.combobox, {
	escapeRegex: function( value ) {
		return value.replace( /([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1" );
	},
	filter: function(array, term) {
		var matcher = new RegExp( $.ui.combobox.escapeRegex(term), "i" );
		return $.grep( array, function(value) {
			return matcher.test( value.label || value.value || value );
		});
	}
});

}( jQuery ));
