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 += "" + hints[iii] + ""; 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 = Math.max(list.scrollHeight, list.offsetHeight); if (!Element.visible(list)){ Element.setStyle(list, { "height": "", "maxHeight": "" }); listHeight = Element.getHeight(list); } var viewportHeight = document.viewport.getHeight(); var containerHeight, containerTop; if (container.getBoundingClientRect){ var rect = container.getBoundingClientRect(); containerHeight = rect.height || rect.bottom - rect.top; containerTop = rect.top; }else{ containerHeight = Element.getHeight(container); 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 || {}); this.scroller = params.scroller || null; Element.addClassName(this.list, this.containerClassName); Event.observe(this.list, "click", this.onclick.bindAsEventListener(this)); Event.observe(this.list, "keypress", this.keyboard.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); if (this.scroller) Element.scrollContainerTo(this.scroller, this.selectedItem); 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)); }, SelectLast: function(){ this.select(this.GetItems().last()); }, SelectNext: function(){ if (!this.selectedItem) return this.SelectFirst(); var next = Element.next(this.selectedItem, this.itemNodeName); if (!next) this.SelectFirst(); else this.SelectItem(next); }, SelectPrev: function(){ if (!this.selectedItem) return this.SelectLast(); var prev = Element.previous(this.selectedItem, this.itemNodeName); if (!prev) this.SelectLast(); else this.SelectItem(prev); }, keyboard: function(event){ switch (event.which || event.keyCode){ case Event.KEY_DOWN: this.SelectNext(); break; case Event.KEY_UP: this.SelectPrev(); break; case Event.KEY_HOME: this.SelectFirst(); break; case Event.KEY_END: this.SelectLast(); break; } } }); SelectableItemsList.events = { "choice": "selectable-items-list:choice", "null-choice": "selectable-items-list:null-choice" };