// This is a port of ajaxdropdown.js to use the jQuery framework instead of the Prototype framework.
var Prototype = {
	emptyFunction: function() { return; }
};
Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};
String.prototype.unescapeHtml = function () {
    var temp = document.createElement("div");
    temp.innerHTML = this;
    var result = temp.childNodes[0].nodeValue;
    temp.removeChild(temp.firstChild)
    return result;
}

var AjaxDropDown = function (properties) {
	var anchorNodePos;
	
	this.options = Object.extend({
		source: null,
		anchor: null,
		offset: null,
		inputElem: null,
		minChars: 3,
		onSuggest: Prototype.emptyFunction,
		onSelect: Prototype.emptyFunction
	}, properties || null);
	
	this.selectedIndex = -1;

	this.source = $("#" + this.options.source);
	this.anchor = $("#" + this.options.anchor);
	this.inputElem = $("#" + this.options.inputElem);
	
	anchorNodePos = this.anchor.offset();
	this.source.css("top", anchorNodePos.top + this.options.offset.top);
	this.source.css("left", anchorNodePos.left + this.options.offset.left);
	this.source.hide();
	
	this.inputElem.bind("blur", this, function(e) { e.data._onBlur(e) });
	this.inputElem.bind("focus", this, function(e) { e.data._onFocus(e) });
	this.source.bind("mousemove", this, function(e) { e.data._onHover(e) });
	this.source.bind("mousedown", this, function(e) { e.data._onMouseDown(e) });
	this.inputElem.bind("keyup", this, function(e) { e.data._onKeyUp(e) });
	this.inputElem.bind("keydown", this, function(e) { e.data._onKeyDown(e) });
	
	return this;
}

AjaxDropDown.prototype = {
	hide: function() {
		this.source.hide();
	},
	
	show: function() {
		this.source.show();
	},
	
	/**
	 * Hides the text suggestions.
	 *
	 * @access private
	 * @return void
	 */
	_onBlur: function(e) {
			this.hide();
	},
	
	/**
	 * Shows the text suggestions.
	 *
	 * @access private
	 * @return void
	 */
	_onFocus: function(e) {
			if (this.source.children("li").length > 0) {
					this.show();
			} else {
					if (this.inputElem.val().length >= this.options.minChars) {
							this.options.onSuggest(this.inputElem.val());
					}
			}
	},

	/**
	 * Highlights the item currently hovered on by the mouse cursor.
	 *
	 * @access private
	 * @return void
	 */
	_onHover: function(e) {
			var hoveredItem = e.target;
			var nodes = this.source.children("li").get();
			if ($(hoveredItem).parent().attr("id") == this.options.source) {
					for (var i = 0; i < nodes.length; i++) {
							if (nodes[i] == hoveredItem) {
									this.selectedIndex = i;
									this._highlightItem();
									return;
							}
					}
			}    
	},
		
	/**
	 * Highlights the selected item specified by this.selectedIndex.
	 *
	 * @access private
	 * @return void
	 */
	_highlightItem: function() {
			var nodes = this.source.children("li");
			for (var i = 0; i < nodes.length; i++) {
					if (i == this.selectedIndex) {
							nodes.eq(i).css("background-color", "#D5DDF3");
					} else {
							nodes.eq(i).css("background-color", "");
					}
			}
	},
	
	/**
	 * Fills the input element with the selected text and hides the suggestions
	 * Calls the callback function with the selected text as a parameter
	 *
	 * @access private
	 * @return void
	 */
	_selectHighlightedItem: function() {
	    if (this.selectedIndex != -1) {
				var nodes = this.source.children("li").get();
				var selectedNode = nodes[this.selectedIndex];
				this.inputElem.val($(selectedNode).text());
				this.hide();
				this.options.onSelect(this.inputElem.val());
			}
	},
	
	/**
	 * Same behaviour as hitting "TAB" or "RETURN" on a selected item.
	 * Event handler for a mousedown event on a text suggestion.
	 *
	 * @access private
	 * @return void
	 */
	_onMouseDown: function(e) {
			var clickedItem = e.target;
			var nodes = this.source.children("li").get();
			if ($(clickedItem).parent().attr("id") == this.options.source) {
					for (var i = 0; i < nodes.length; i++) {
							if (nodes[i] == clickedItem) {
									this.selectedIndex = i;
									this._selectHighlightedItem();
									// To prevent propagation to onBlur
									e.preventDefault();
									return;
							}
					}
			}
	},
	
 /**
	 * Callback method for external suggestions generation. This generates the
	 * list of text suggestions from the parameter textArray.
	 *
	 * @param string[] list of suggestions
	 * @access public
	 * @return void
	 */
	generateSuggestions: function(textArray) {
			var nodeList, node;
			nodeList = this.source;
			nodeList.html("");
			for (var i = 0; i < textArray.length; i++) {
					node = document.createElement('li');
					$(node).html(textArray[i].toString());
					nodeList.append("<li>" + $(node).html() + "</li>");
			}
			this.selectedIndex = -1;
			this.show();
	},
	
	/**
	 * Event handler for keyup event on the input text field. This will
	 * trap changes in the input box and generate new text suggestions.
	 *
	 * @access private
	 * @return void
	 */
	_onKeyUp: function(event) {
			var k = event.keyCode;
			if (this.inputElem.val().length >= this.options.minChars && 
					((k >= 65 && k <= 90) || (k >= 97 && k <= 122) ||
					k == 8 || k == 46)) {
					this.options.onSuggest(this.inputElem.val());
			}
	},
	
	/**
	 * Event handler for keydown event on the input text field. Captures up, down, 
	 * return and tab key events for standard behaviour in traversing and selecting
	 * items from the list of text suggestions. This has the side effect of stopping
	 * the event from propagating if the key is TAB or RETURN.
	 *
	 * @access private
	 * @return void
	 */
	_onKeyDown: function(e) {
			var k = e.keyCode;
			switch (k) {
					case 40:
							this.selectedIndex = Math.min(
									this.source.children("li").length - 1, 
									this.selectedIndex + 1);
							this._highlightItem();
							break;
					case 38:
							this.selectedIndex = Math.max(0, this.selectedIndex - 1);
							this._highlightItem();
							break;
					case 27:
							this.hide();
							break;
					case 33:
							this.selectedIndex = 0;
							this._highlightItem();
							e.preventDefault();
							break;
					case 34:
							this.selectedIndex = this.source.children("li").length - 1;
							this._highlightItem();
							e.preventDefault();
							break;
					case 9:
					case 13:
							// check if user is currently navigating the dropdown, otherwise
							// they may be trying to tab to another element or submit the form
							if (this.source.css("display") != "none" && this.selectedIndex != -1) {
									this._selectHighlightedItem();
									e.preventDefault();
							}
							break;
			}
	}
};