// LookUp.js // Javascript Behaviour for the LookUp Control // Copyright (c) by Matthias Hertel, http://www.mathertel.de // This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx // ----- // 12.08.2005 created by Matthias Hertel // 21.09.2005 more robust whan fast typing. // clicking into the dropdown list enabled. // 29.08.2006 memory leak removed // 16.09.2006 context on event-methods is now set to the bound object. // 18.12.2007 Simplifications and documentation. var LookUpBehaviour = { lookupservice: null, // must be set on the control ! eventname: "", /// <summary>The local or complete event name that is used for publishing OpenAjax events.</summary> savevalue: null, dd: null, // drop down object // setup the control init: function () { if ((this.eventname != null) && (this.eventname != "")) { OpenAjax.hub.subscribe(jcl.BuildFullEventname(this), this._handleEvent, this); } // if // late binding to the WebService. Only the alias is known for now if ((this.lookupservice == null) || (this.lookupservice == "")) { alert("Error: no lookupservice specified for LookUpControl " + this.id + "!"); } else { this._fillAction = jcl.CloneObject(this._fillAction); this._fillAction.call = proxies[this.lookupservice].GetPrefixedEntries; } // if }, // clear up the control term: function () { this.RemoveDropdown(); this.savevalue = null; }, // --- OpenAjax event handler --- _handleEvent: function (eventName, eventData) { if (this.value != eventData) { this.value = eventData; this.typedText = eventData; } // if }, // _handleEvent // declare an AJAX action to the lookup service _fillAction: { delay: 100, prepare: function (fld) { fld.savevalue = fld.value; return (fld.value); }, call: null, // is assigned later finish: function (val, fld) { if (fld.savevalue != fld.value) { ajax.Start(fld._fillAction, fld); // again } else if ((val != null) && (val.indexOf(';') < 0)) { if (fld.dd != null) { fld.SetUntypedText(val); fld.RemoveDropdown(); } } else { var dd = fld.CreateDropdown(fld); fld.FillDropdown(dd, val); // if (jcl.isIE) // fld.NextDropdownEntry(fld); } // if }, // finish onException: proxies.alertException }, // _fillAction // attach to the keyDown event // extend the current selection if backspace (8) is pressed. onkeydown: function (evt) { evt = evt || window.event; if (jcl.isIE) { var r = document.selection.createRange(); if (evt.keyCode == 8) { r.moveStart("character", -1); r.select(); } // if } else { // Firefox } // if }, // onkeydown // attach to the keyUp event // handle all keyboard events for the dropdown list onkeyup: function (evt) { evt = evt || window.event; var t = evt.target || evt.srcElement; if (evt.keyCode == 40) { // down arrow if (t.dd == null) ajax.Start(t._fillAction, t); else t.NextDropdownEntry(t); } else if (evt.keyCode == 38) { // up arrow if (t.dd == null) { // is already closed } else if (t.dd.selected == t.dd.firstChild) { t.RemoveDropdown(); } else { t.PreviousDropdownEntry(t); } // if } else if ((evt.keyCode == 27) || (evt.keyCode == 13)) { // escape or return if (t.dd != null) t.RemoveDropdown(); } else if ((evt.keyCode == 8) || (evt.keyCode >= 32)) { if ((t.typedText == null) || (t.typedText != t.value)) { t.TypeText(t.value); } // if } // if }, // onkeyup // close the dropdown list when leaving the field // publish the current value onblur: function (evt) { evt = evt || window.event; var t = evt.target || evt.srcElement; // window.setTimeout(t.RemoveDropdown.bind(t), 1000); t.RemoveDropdown(); OpenAjax.hub.publish(jcl.BuildFullEventname(this), this.value); }, // handleBlur // click into the dropdown rectange. // the event handler is attached dynamically _onMouseDown: function (evt) { evt = evt || window.event; var t = evt.target || evt.srcElement; var fld = t.parentNode.fld; fld.TypeText(t.innerText); // cancel selecting anything evt.cancelBubble = true; evt.returnValue = false; }, // _onMouseDown // ----- methods ----- // Open or close the dropdown ToggleDropdown: function () { if (this.dd == null) ajax.Start(this._fillAction, this); else this.RemoveDropdown(); }, // ToggleDropdown // Create a absolute positioned <div> element for the drop-down content // using a fields dimensions. // The created object is returned and is also attachet to the input field using the dd property. CreateDropdown: function (fld) { var dd = fld.dd; var top, left; var ddImg = fld.nextSibling; if (ddImg.nodeType != 1) ddImg = ddImg.nextSibling; if (dd == null) { // create dropdown dd = window.document.createElement("div"); fld.parentNode.insertBefore(dd, fld); fld.dd = dd; dd.fld = fld; dd.style.position = "absolute"; top = (fld.offsetTop + fld.offsetHeight); left = fld.offsetLeft; op = fld.offsetParent; while (op != null) { top += op.offsetTop; left += op.offsetLeft; op = op.offsetParent; } // while dd.style.top = top + "px"; dd.style.left = left + "px"; dd.style.width = (fld.clientWidth + ddImg.clientWidth) + "px"; dd.style.height = "200px"; dd.style.border = "solid 1px black"; dd.style.borderTop = "0px"; dd.style.backgroundColor = "#DDDDEE"; dd.style.overflowY = "scroll"; dd.style.display = "block"; jcl.AttachEvent(dd, "onmousedown", this._onMouseDown); } // if return (dd); }, // CreateDropdown // Fill the dropdown list with new values. FillDropdown: function (dd, options) { if (typeof (options) == "string") options = options.split(";"); // clear existing options while (dd.firstChild != null) dd.removeChild(dd.firstChild); if (options != null) { for (var n = 0; n < options.length; n++) { o = document.createElement("div"); o.innerText = options[n]; dd.appendChild(o); } // for } // if dd.selected = null; }, // FillDropdown // Remove the dropdown list RemoveDropdown: function () { var dd = this.dd; if (dd != null) { jcl.DetachEvent(dd, "onmousedown", this._onMouseDown); dd.fld = null; dd.parentNode.removeChild(dd); this.dd = null; } // if }, // RemoveDropdown // Select the next entry in the dropdown list. NextDropdownEntry: function () { var dd = this.dd; if (dd.selected == null) { dd.selected = dd.firstChild; } else if (dd.selected.nextSibling != null) { dd.selected.style.backgroundColor = "transparent"; dd.selected.style.color = "black"; dd.selected = dd.selected.nextSibling; } // if if (dd.selected != null) { dd.selected.style.backgroundColor = "black"; dd.selected.style.color = "white"; dd.selected.scrollIntoView(false); this.SetUntypedText(dd.selected.innerText); } // if }, // NextDropdownEntry // Select the previous entry in the dropdown list. PreviousDropdownEntry: function () { var dd = this.dd; if (dd.selected.previousSibling != null) { dd.selected.style.backgroundColor = "transparent"; dd.selected.style.color = "black"; dd.selected = dd.selected.previousSibling; dd.selected.style.backgroundColor = "black"; dd.selected.style.color = "white"; dd.selected.scrollIntoView(true); this.SetUntypedText(dd.selected.innerText); } // if }, // PreviousDropdownEntry // set the text of field and start a new LookUp TypeText: function (aText) { this.value = aText; this.typedText = aText; ajax.Start(this._fillAction, this); }, // TypeText // Extend the text in the input field by some characters. // keep the typed text part unselected so typing is overriding the additional text. SetUntypedText: function (aText) { var tmp = this.typedText; this.value = aText; if (jcl.isIE) { var r = this.createTextRange(); if (tmp == null) { // select all } else if (tmp.toLowerCase() == aText.substr(0, tmp.length).toLowerCase()) { r.moveStart('character', tmp.length); } // if r.select(); } else { // Firefox if (tmp == null) { this.setSelectionRange(0, aText.length); } else { this.setSelectionRange(tmp.length, aText.length); } } // if } // SetUntypedText }; // LookUpBehaviour
This page is part of the http://www.mathertel.de/ web site.
For updates and discussions see http://ajaxaspects.blogspot.com/.