var ItemsList = Class.create({
	initialize: function(list){
		this.list = $(list);
		if (!this.list) this.list = new Element("ul", {"class": "DropDown"});
		Element.setStyle(this.list, {"display": "none"});
	},

	checkElement: function(element){
		return element == this.list || Element.descendantOf(element, this.list);
	},

	onDocumentMouseDown: function(event){
		var element = Event.element(event);
		if (this.list.isOpen && element != this.list && !Element.descendantOf(element, this.list)) this.setClosed();
	},

	testItem: function(element){ return element && Element.descendantOf(element, this.list) && Element.match(element,  "li"); },

	highlightingOn: function(event){
		var li = Event.findElement(event, "li");
		if (this.testItem(li)) this.changeHighlighting(li, true);
	},

	highlightingOff: function(event){
		var li = Event.findElement(event, "li");
		if (this.testItem(li)) this.changeHighlighting(li, false);
	},

	GetHighlighted: function(){ return this.highlightedItem; },
	SetHighlighted: function(element) {
		Element.addClassName(element, "Active");
		this.highlightedItem = element;
	},

	changeHighlighting: function(element, highlight){
		if (highlight){
			if (!element || this.highlightedItem == element) return;
			if (this.highlightedItem) this.changeHighlighting(this.highlightedItem, false);
			this.highlightedItem = element;
			if (this.highlightedItem){
				Element.addClassName(this.highlightedItem, "Active");
				Element.scrollContainerTo(this.list, this.highlightedItem);
			}
		}else{
			if (!element || this.highlightedItem != element) return;
			if (!this.highlightedItem) return;
			Element.removeClassName(this.highlightedItem, "Active");
			this.highlightedItem = null;
		}
	},

	highlightNext: function(){
// 		if (!this.list.isOpen) return this.setOpen();
		if (!this.highlightedItem || !Element.visible(this.highlightedItem)) return this.highlightFirst();
		var nextItem = Element.nextSiblings(this.highlightedItem).find(Element.visible);
		this.changeHighlighting(nextItem, true);
	},

	highlightPrev: function(){
// 		if (!this.list.isOpen) return this.setOpen();
		if (!this.highlightedItem || !Element.visible(this.highlightedItem)) return this.highlightLast();
		var nextItem = Element.previousSiblings(this.highlightedItem).find(Element.visible);
		this.changeHighlighting(nextItem, true);
	},

	getFirstItem: function(){ return Element.childElements(this.list).find(Element.visible); },

	highlightFirst: function(){
		this.changeHighlighting(this.getFirstItem(), true);
	},

	highlightLast: function(){
		this.changeHighlighting(Element.childElements(this.list).reverse().find(Element.visible), true);
	},

	refreshListPosition: function(){
		if (typeof this.pos == "undefined") this.pos = {"left": 0, "top": 0};
		Element.setStyle(this.list, {"top": this.pos.top + "px", "left": this.pos.left + "px"});
	},

	refreshListPositionIfOpen: function(event){
		var element = Event.element(event);
		if (!this.list.isOpen) return;
		if (Prototype.Browser.IE || !element || element != this.list && !Element.descendantOf(element, this.list)){
			if (typeof this.deferredRefresh != "undefined") window.clearTimeout(this.deferredRefresh);
			this.deferredRefresh = this.refreshListPosition.bind(this).defer();
		}
	},

	setOpen: function(){
		if (this.list.isOpen) return;
		if (typeof this.callbacks_ == "undefined"){
			var resize = this.refreshListPositionIfOpen.bindAsEventListener(this);
			this.callbacks_ = [
				[this.list, "mouseover", this.highlightingOn.bindAsEventListener(this)],
				[this.list, "mouseup", this.onClickList.bindAsEventListener(this)],
				[document, "mousedown", this.onDocumentMouseDown.bindAsEventListener(this)],
				[window, "resize", resize],
				[document, "mousewheel", resize],
				[document, "DOMMouseScroll", resize]
			];
		}
		this.callbacks_.each(function(args){ Event.observe.apply(null, args); });

		Element.setStyle(this.list, {"display": "", "visibility": ""});
		this.refreshListPosition();
		this.list.isOpen = true;
	},

	setClosed: function(){
		if (!this.list.isOpen) return;
		Element.setStyle(this.list, {"display": "none"});
		if (typeof this.callbacks_ != "undefined")
			this.callbacks_.each(function(args){ Event.stopObserving.apply(null, args); });
		this.list.isOpen = false;
	},

	toggleOpen: function(){
		this[this.list.isOpen ? "setClosed" : "setOpen"]();
	},

	onClickList: function(event){
		var li = Event.findElement(event, "li");
		if (!this.testItem(li)) return;
		Element.fire(li, ItemsList.events["choice"]);
		Event.stop(event);
	},

	ClearList: function(){ Element.clear(this.list); },

	AddItems: function(array, keys, hints) {
		$A(array).each(function(item, iii){
			var str = String(item.label || item);
			if (hints) str += "<span>" + hints[iii] + "</span>";
			var li = new Element("li").update(str);
			li.key = keys ? keys[iii] : str;
			li.item = item;
			this.list.appendChild(li);
		}.bind(this));
	},

	RefreshList: function(array, keys, hints){
		this.ClearList();
		if (array) this.AddItems(array, keys, hints);
	},

	AppendItem: function(li){
		if (!li || !Element.match(li, "li")) return;
		this.list.appendChild(li);
	},

	GetItems: function(){ return Element.childElements(this.list); },
	IsOpen: function(){ return this.list.isOpen; }
});

ItemsList.events = { "choice": "items-list:choice" };

ItemsList.getOptimalPosition = function(container, list){
	var listHeight = list.scrollHeight;
	if (!Element.visible(list)){
		Element.setStyle(list, { "height": "", "maxHeight": "" });
		listHeight = Element.getHeight(list);
	}
	var viewportHeight = document.viewport.getHeight();
	var containerHeight = Element.getHeight(container);
	var containerTop = Element.viewportOffset(container)["top"];

	var result = { "offsetTop": containerHeight };
	if (containerTop + containerHeight + listHeight > viewportHeight){ // matches down
		if (containerTop >= listHeight) result["offsetTop"] = -listHeight; // matches up
		else if(viewportHeight-containerTop-containerHeight>containerTop) { // partially down
			result["maxHeight"] = viewportHeight - (containerTop + containerHeight);
		}
		else { // partially up
			result["offsetTop"] = -containerTop;
			result["maxHeight"] = containerTop;
		}
	}
	return result;
};

var ItemsListAligned = Class.create(ItemsList, {
	initialize: function($super, list, alignmentBase){
		this.alignmentBase = $(alignmentBase);
		if (!this.alignmentBase) return alert("ItemsListPositioned: alignment base not specified");
		$super(list);
	},

	getOptimalPosition: ItemsList.getOptimalPosition,

	refreshListPosition: function(){
		var scrollTop = this.list.scrollTop;
		var pos = this.getOptimalPosition(this.alignmentBase, this.list);
		Element.clonePosition(this.list, this.alignmentBase, {
			"setHeight": false,
			"setWidth": false,
			"offsetTop": pos.offsetTop
		});
		Element.setStyle(this.list, {
			"width": this.alignmentBase.clientWidth + "px",
			"maxHeight": typeof pos.maxHeight == "undefined" ? "" : pos.maxHeight + "px"
		});
		if (scrollTop != 0) this.list.scrollTop = scrollTop;
	},

	onDocumentMouseDown: function($super, event){
		var element = Event.element(event);
		if (!element || element != this.alignmentBase && !Element.descendantOf(element, this.alignmentBase))
			$super(event);
		else Event.stop(event);
	}
});

var SelectableItemsList = Class.create({
	initialize: function(list, params){
		this.list = $(list);
		if (!this.list) return;
		Object.extend(this, params || {});
		Element.addClassName(this.list, this.containerClassName);
		Event.observe(this.list, "click", this.onclick.bindAsEventListener(this));
	},

	containerClassName: "ItemsList",
	selectedClassName: "Active",
	itemNodeName: "li",
	nullIsSelectable: true,

	selectedItem: null,

	testElement: function(element){
		return element && Element.descendantOf(element, this.list) && Element.match(element, this.itemNodeName);
	},

	select: function(item){
		 if (!this.testElement(item) && !this.nullIsSelectable) return;
		 if (this.selectedItem == item) return;
		 Element.removeClassName(this.selectedItem, this.selectedClassName);
		 this.selectedItem = item;
		 if (this.selectedItem){
			Element.addClassName(this.selectedItem, this.selectedClassName);
			Element.fire(this.selectedItem, SelectableItemsList.events["choice"]);
		 }else Element.fire(this.list, SelectableItemsList.events["null-choice"]);
	},

	onclick: function(event){ this.select(Event.findElement(event, this.itemNodeName)); },

	GetItems: function(){ return Element.select(this.list, this.itemNodeName); },

	SelectItem: function(item){ this.select(item); },

	SelectFirst: function(){ this.select(Element.down(this.list, this.itemNodeName)); }
});

SelectableItemsList.events = {
	"choice": "selectable-items-list:choice",
	"null-choice": "selectable-items-list:null-choice"
};

