var MultiSelect = Class.create(JSControl, { initialize: function($super, element){ $super(element); this.handlers_ = [ [this.element, "mousedown", this.mousedown.bindAsEventListener(this)], [this.element, "selectstart", Event.stop], [this.element, Prototype.Browser.WebKit || Prototype.Browser.IE ? "keydown" : "keypress", this.keyboard.bindAsEventListener(this)] ]; this.handlers_.each(function(args){ Event.observe.apply(null, args); }); this.set_focused(this.first_item()); }, get_item: function(event){ var li = Event.findElement(event, "li"); if (!li || li.parentNode != this.element) return null; else return li; }, set_focused: function(elem){ if (this.focused_ == elem) return; if (this.focused_){ Element.removeClassName(this.focused_, "focus"); this.focused_ = null; } if (elem){ Element.addClassName(elem, "focus"); this.focused_ = elem; Element.scrollContainerTo(this.element, elem); } }, selected_classname: "Selected", toggle_selected: function(elem){ if (!elem) return; Element[Element.hasClassName(elem, this.selected_classname) ? "removeClassName" : "addClassName"](elem, this.selected_classname); }, clear_selection: function(){ for (var elem = this.element.firstChild; elem; elem = elem.nextSibling){ if (elem.nodeType == Node.ELEMENT_NODE) Element.removeClassName(elem, this.selected_classname); } }, set_selected: function(elem){ if (elem) Element.addClassName(elem, this.selected_classname); }, set_selected_interval: function(li1, li2){ var count = 0; for (var elem = this.element.firstChild; elem; elem = elem.nextSibling){ if (elem.nodeType != Node.ELEMENT_NODE) continue; var is_bounder_1 = elem == li1; if (is_bounder_1) ++count; var is_bounder_2 = elem == li2; if (is_bounder_2) ++count; var is_bounder = is_bounder_1 || is_bounder_2; Element[count == 1 || count == 2 && is_bounder ? "addClassName" : "removeClassName"](elem, this.selected_classname); } }, add_selected_interval: function(li1, li2){ var count = 0; for (var elem = this.element.firstChild; elem; elem = elem.nextSibling){ if (elem.nodeType != Node.ELEMENT_NODE) continue; var is_bounder_1 = elem == li1; if (is_bounder_1) ++count; var is_bounder_2 = elem == li2; if (is_bounder_2) ++count; var is_bounder = is_bounder_1 || is_bounder_2; if (count == 1 || count == 2 && is_bounder) Element.addClassName(elem, this.selected_classname); } }, mousedown: function(event){ var li = this.get_item(event); if (!li) return; this.Focus(); this.value_mousedown_ = this.Value(); this.set_focused(li); if (event.shiftKey){ if (this.rel_) this[event.ctrlKey ? "add_selected_interval" : "set_selected_interval"](this.rel_, li); else{ this.clear_selection(); this.set_selected(li); this.rel_ = li; } }else if (event.ctrlKey){ this.toggle_selected(li); this.rel_ = li; }else{ this.clear_selection(); this.set_selected(li); this.rel_ = li; } if (!this.list_handlers_) this.list_handlers_ = [ [this.element, "mouseover", this.mouseover.bindAsEventListener(this)], [document, "mouseup", this.mouseup.bindAsEventListener(this)], [this.element, "mouseout", this.mouseout.bindAsEventListener(this)] ]; this.list_handlers_.each(function(args){ Event.observe.apply(null, args); }); if (event.preventDefault) event.preventDefault(); }, process_mouseover: function(li, event){ if (!li || this.focused_ == li) return; if (event.ctrlKey){ this.toggle_selected(li); this.rel_ = li; }else{ if (!this.rel_) this.rel_ = li; this.set_selected_interval(this.rel_, li); } this.set_focused(li); }, mouseover: function(event){ this.stop_scroll(); this.process_mouseover(this.get_item(event), event); }, mouseup: function(event){ this.stop_list_handlers(); this.stop_scroll(); if (URLSerialize(this.value_mousedown_) != URLSerialize(this.Value())) this.onchange(); }, stop_list_handlers: function(){ if (!this.list_handlers_) return; this.list_handlers_.each(function(args){ Event.stopObserving.apply(null, args); }); this.stop_scroll(); }, mouseout: function(event){ var rel_target = event.relatedTarget || event.toElement; if (rel_target && rel_target != this.element && !Element.descendantOf(rel_target, this.element)){ this.start_scroll(event); this.update_scroll(event); } }, start_scroll: function(event){ if (!this.scroller_handlers_) this.scroller_handlers_ = [[document, "mousemove", this.update_scroll.bindAsEventListener(this)]]; this.scroller_handlers_.each(function(args){ Event.observe.apply(null, args); }); }, stop_scroll: function(){ if (this.scroller_handlers_) this.scroller_handlers_.each(function(args){ Event.stopObserving.apply(null, args); }); }, update_scroll: function(event){ if (!this.focused_) return; var cur = {"x": Event.pointerX(event), "y": Event.pointerY(event)}; var pos = Element.cumulativeOffset(this.element); var hei = this.element.offsetHeight; var tofocus = null; if (cur.y < pos.top){//scroll up for (var elem = this.focused_.previousSibling; elem; elem = elem.previousSibling) if (elem.nodeType == Node.ELEMENT_NODE){ tofocus = elem; break; } }else if (cur.y > pos.top + hei){//scroll down for (var elem = this.focused_.nextSibling; elem; elem = elem.nextSibling) if (elem.nodeType == Node.ELEMENT_NODE){ tofocus = elem; break; } } if (tofocus) this.process_mouseover(tofocus, event); }, init_dynamic: function(apacket){ var data = apacket.Data(); if (data.length == 0) return; var val_colnam = this.element.getAttribute("key-column") || this.element.getAttribute("key") || ""; var lbl_colnam = this.element.getAttribute("show") || ""; var header = data.shift(); var val_col = -1; var lbl_col = -1; for (var iii = 0; iii < header.length; ++iii){ if (header[iii] == val_colnam) val_col = iii; if (header[iii] == lbl_colnam) lbl_col = iii; if (val_col >= 0 && lbl_col >= 0) break; } if (val_col < 0 || lbl_col < 0) return; this.loaded_ = true; var old_value = this.Value(); Element.clear(this.element); data.each(function(row){ var li = document.createElement("li"); li.setAttribute("value", URLSerialize(row[val_col])); li.appendChild(document.createTextNode(row[lbl_col])); this.element.appendChild(li); }, this); this.SetValue(old_value); if (typeof this.value_ != "undefined") delete this.value_; }, SetAttribute: function($super, nam, val){ switch (nam){ case "src": if (val == this.src_) break; this.src_ = val; this.dynamic_ = true; this.loaded_ = false; new ServerCall(val, null, {"onSuccess": this.init_dynamic, "thisObject": this}); break; case "visibility": if (!["yes", "ro", "vo", "link", "no"].include(val)) break; if (typeof this.visibility_ == "undefined") this.visibility_ = "yes"; if (this.visibility_ == val) break; Element.setStyle(this.element, {"display": val == "no" ? "none" : ""}); Element[val == "vo" || val == "link" ? "addClassName" : "removeClassName"](this.element, "VO"); Element[val == "link" ? "addClassName" : "removeClassName"](this.element, "Link"); if (this.visibility_ == "yes" && val != "yes"){ this.handlers_.each(function(args){ Event.stopObserving.apply(null, args); }); delete this.element.tabIndex; } else if (this.visibility_ != "yes" && val == "yes"){ this.handlers_.each(function(args){ Event.observe.apply(null, args); }); if (this.tabbase_) this.element.tabIndex = this.tabbase_; } this.visibility_ = val; break; case "key-column": case "key": case "show": case "key-type": this.element.setAttribute(nam, val); break; default: $super(nam, val); } }, strs2vals: function(vals){ if (this.dynamic_) return vals.collect(URLRestore); else switch (this.element.getAttribute("key-type")){ case "int": return vals.collect(function(str){ return int(parseInt(str, 10)); }); case "int64": return vals.collect(function(str){ return int64(parseInt(str, 10)); }); case "string": default: return vals; } }, val2str: function(val){ if (val instanceof Optional) val = val.Value(); return String(val); }, vals2strs: function(vals){ if (this.dynamic_) return vals.collect(URLSerialize); else return vals.collect(this.val2str, this); }, Value: function(){ if (this.value_) return this.value_; var vals = []; for (var elem = this.element.firstChild; elem; elem = elem.nextSibling) if (elem.nodeType == Node.ELEMENT_NODE && Element.hasClassName(elem, this.selected_classname)) vals.push(elem.getAttribute("value") || ""); return this.strs2vals(vals); }, first_item: function(){ var elem = this.element.firstChild; while (elem && elem.nodeType != Node.ELEMENT_NODE) elem = elem.nextSibling; return elem || null; }, last_item: function(){ var elem = this.element.lastChild; while (elem && elem.nodeType != Node.ELEMENT_NODE) elem = elem.previousSibling; return elem || null; }, next_item: function(li){ if (!li) return null; li = li.nextSibling; while (li && li.nodeType != Node.ELEMENT_NODE) li = li.nextSibling; return li || null; }, prev_item: function(li){ if (!li) return null; li = li.previousSibling; while (li && li.nodeType != Node.ELEMENT_NODE) li = li.previousSibling; return li || null; }, SetValue: function(val){ if (this.dynamic_ && !this.loaded_){ this.value_ = val; return; } var strs = this.vals2strs($A(val)); for (var elem = this.element.firstChild; elem; elem = elem.nextSibling) if (elem.nodeType == Node.ELEMENT_NODE) Element[strs.include(elem.getAttribute("value")) ? // "addClassName" : "removeClassName"](elem, this.selected_classname); if (!this.rel_ || this.rel_.parentNode != this.element || // !Element.hasClassName(this.rel_, this.selected_classname)){ this.rel_ = null; for (var elem = this.element.firstChild; elem; elem = elem.nextSibling) if (elem.nodeType == Node.ELEMENT_NODE && Element.hasClassName(elem, this.selected_classname)){ this.rel_ = elem; break; } } if (!this.focused_ || this.focused_.parentNode != this.element){ this.set_focused(null); for (var elem = this.element.lastChild; elem; elem = elem.previousSibling) if (elem.nodeType == Node.ELEMENT_NODE && Element.hasClassName(elem, this.selected_classname)){ this.set_focused(elem); break; } if (!this.focused_) this.set_focused(this.first_item()); } }, Focus: function(){ this.element.focus(); }, keyboard: function(event){ if (!event) return; if (this.focused_ && this.focused_.parentNode != this.element) this.set_focused(null); var tofocus = null; switch (event.which || event.keyCode){ case Event.KEY_DOWN: case Event.KEY_RIGHT: tofocus = this.next_item(this.focused_); break; case Event.KEY_UP: case Event.KEY_LEFT: tofocus = this.prev_item(this.focused_); break; case Event.KEY_HOME: case Event.KEY_PAGEUP: tofocus = this.first_item(); break; case Event.KEY_END: case Event.KEY_PAGEDOWN: tofocus = this.last_item(); break; case 32: //SPACE if (this.focused_) this.toggle_selected(this.focused_); break; case Event.KEY_RETURN: if (!event.ctrlKey && this.focused_ && this.focused_.parentNode == this.element){ this.clear_selection(); this.set_selected(this.focused_); } break; default: return; } if (tofocus){ this.set_focused(tofocus); if (event.ctrlKey) this.rel_ = tofocus; else{ if (event.shiftKey && this.rel_ && this.rel_.parentNode == this.element) this.set_selected_interval(this.rel_, tofocus); else{ this.clear_selection(); this.set_selected(tofocus); this.rel_ = tofocus; } } } if (event.preventDefault) event.preventDefault(); }, SetTabOrder: function(tabbase){ this.tabbase_ = tabbase; if (this.visibility_ && this.visibility_ != "yes") return tabbase++; else{ this.element.tabIndex = tabbase++; return tabbase; } } });