diff -r 41c45314ac27 -r 5e8fd89c02ea includes/clientside/static/SpryAutoSuggest.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/includes/clientside/static/SpryAutoSuggest.js Sun Jun 22 18:13:59 2008 -0400 @@ -0,0 +1,883 @@ +// SpryAutoSuggest.js - version 0.91 - Spry Pre-Release 1.6.1 +// +// Copyright (c) 2006. Adobe Systems Incorporated. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Adobe Systems Incorporated nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +var Spry; +if (!Spry) Spry = {}; +if (!Spry.Widget) Spry.Widget = {}; + +Spry.Widget.BrowserSniff = function() +{ + var b = navigator.appName.toString(); + var up = navigator.platform.toString(); + var ua = navigator.userAgent.toString(); + + this.mozilla = this.ie = this.opera = this.safari = false; + var re_opera = /Opera.([0-9\.]*)/i; + var re_msie = /MSIE.([0-9\.]*)/i; + var re_gecko = /gecko/i; + var re_safari = /(applewebkit|safari)\/([\d\.]*)/i; + var r = false; + + if ( (r = ua.match(re_opera))) { + this.opera = true; + this.version = parseFloat(r[1]); + } else if ( (r = ua.match(re_msie))) { + this.ie = true; + this.version = parseFloat(r[1]); + } else if ( (r = ua.match(re_safari))) { + this.safari = true; + this.version = parseFloat(r[2]); + } else if (ua.match(re_gecko)) { + var re_gecko_version = /rv:\s*([0-9\.]+)/i; + r = ua.match(re_gecko_version); + this.mozilla = true; + this.version = parseFloat(r[1]); + } + this.windows = this.mac = this.linux = false; + + this.Platform = ua.match(/windows/i) ? "windows" : + (ua.match(/linux/i) ? "linux" : + (ua.match(/mac/i) ? "mac" : + ua.match(/unix/i)? "unix" : "unknown")); + this[this.Platform] = true; + this.v = this.version; + + if (this.safari && this.mac && this.mozilla) { + this.mozilla = false; + } +}; + +Spry.is = new Spry.Widget.BrowserSniff(); + +Spry.Widget.AutoSuggest = function(region, sRegion, dataset, field, options) +{ + if (!this.isBrowserSupported()) + return; + + options = options || {}; + + this.init(region, sRegion, dataset, field); + Spry.Widget.Utils.setOptions(this, options); + + if (Spry.Widget.AutoSuggest.onloadDidFire) + this.attachBehaviors(); + else + Spry.Widget.AutoSuggest.loadQueue.push(this); + + // when data is changing we will decide if we will have to show the suggestions + this.dataset.addObserver(this); + + // Set up an observer so we can attach our click behaviors whenever + // the region is regenerated. + var regionID = Spry.Widget.Utils.getElementID(sRegion); + + var self = this; + this._notifyDataset = { onPostUpdate: function() { + self.attachClickBehaviors(); + }, onPreUpdate: function(){ + self.removeClickBehaviours(); + }}; + + Spry.Data.Region.addObserver(regionID,this._notifyDataset); + + // clean up the widget when on page unload + Spry.Widget.Utils.addEventListener(window, 'unload', function(){self.destroy()}, false); + + // make the first computation in case the textfield is not empty + this.attachClickBehaviors(); + this.handleKeyUp(null); + this.showSuggestions(false); +}; + +Spry.Widget.AutoSuggest.prototype.init = function(region, sRegion, dataset, field) +{ + this.region = Spry.Widget.Utils.getElement(region); + + if (!this.region) + return; + + this.minCharsType = false; + this.containsString = false; + this.loadFromServer = false; + this.urlParam = ''; + this.suggestionIsVisible = false; + this.stopFocus = false; + this.hasFocus = false; + this.showSuggestClass = 'showSuggestClass'; + this.hideSuggestClass = 'hideSuggestClass'; + this.hoverSuggestClass = 'hoverSuggestClass'; + this.movePrevKeyCode = Spry.Widget.AutoSuggest.KEY_UP; + this.moveNextKeyCode = Spry.Widget.AutoSuggest.KEY_DOWN; + + this.textElement = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.region, "INPUT"); + this.textElement.setAttribute('AutoComplete', 'off'); + + this.suggestRegion = Spry.Widget.Utils.getElement(sRegion); + // prepare the suggest region + Spry.Widget.Utils.makePositioned(this.suggestRegion); + Spry.Widget.Utils.addClassName(this.suggestRegion, this.hideSuggestClass); + + this.timerID = null; + if (typeof dataset == "string"){ + this.dataset = window[dataset]; + }else{ + this.dataset = dataset; + } + this.field = field; + if (typeof field == 'string' && field.indexOf(',') != -1) + { + field = field.replace(/\s*,\s*/ig, ','); + this.field = field.split(','); + } +}; + +Spry.Widget.AutoSuggest.prototype.isBrowserSupported = function() +{ + return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows + || + Spry.is.mozilla && Spry.is.v >= 1.4 + || + Spry.is.safari + || + Spry.is.opera && Spry.is.v >= 9; +}; + +Spry.Widget.AutoSuggest.prototype.getValue = function() +{ + if (!this.textElement) + return ''; + return this.textElement.value; +}; + +Spry.Widget.AutoSuggest.prototype.setValue = function(str) +{ + if (!this.textElement) + return; + this.textElement.value = str; + this.showSuggestions(false); +}; + +Spry.Widget.AutoSuggest.prototype.focus = function() +{ + if (!this.textElement) + return; + this.textElement.focus(); +}; + +Spry.Widget.AutoSuggest.prototype.showSuggestions = function(doShow) +{ + if (this.region && this.isVisibleSuggestion() != doShow) + { + if (doShow && this.hasFocus) + { + Spry.Widget.Utils.addClassName(this.region, this.showSuggestClass); + if (Spry.is.ie && Spry.is.version < 7) + this.createIframeLayer(this.suggestRegion); + } + else + { + if (Spry.is.ie && Spry.is.version < 7) + this.removeIframeLayer(); + Spry.Widget.Utils.removeClassName(this.region, this.showSuggestClass); + } + } + this.suggestionIsVisible = Spry.Widget.Utils.hasClassName(this.region, this.showSuggestClass); +}; + +Spry.Widget.AutoSuggest.prototype.isVisibleSuggestion = function() +{ + return this.suggestionIsVisible; +}; + +Spry.Widget.AutoSuggest.prototype.onDataChanged = function(el) +{ + var data = el.getData(true); + var val = this.getValue(); + this.showSuggestions(data && (!this.minCharsType || val.length >= this.minCharsType) && (data.length > 1 || (data.length == 1 && this.childs[0] && this.childs[0].attributes.getNamedItem("spry:suggest").value != this.getValue()))); +}; +Spry.Widget.AutoSuggest.prototype.nodeMouseOver = function(e, node) +{ + var l = this.childs.length; + for (var i=0; i this.scrolParent.scrollTop + h) + { + // the 5 pixels make the latest option more visible. + this.scrolParent.scrollTop = el.offsetTop + el.offsetHeight - h + 5; + if (this.scrolParent.scrollTop < 0) + this.scrolParent.scrollTop = 0; + } + + } +}; + +Spry.Widget.AutoSuggest.KEY_UP = 38; +Spry.Widget.AutoSuggest.KEY_DOWN = 40; + +Spry.Widget.AutoSuggest.prototype.handleSpecialKeys = function(e){ + + switch (e.keyCode) + { + case this.moveNextKeyCode: // Down key + case this.movePrevKeyCode: // Up Key + if (!(this.childs.length > 0) || !this.getValue()) + return; + + var prev = this.childs.length-1; + var next = false; + var found = false; + var data = this.dataset.getData(); + if (this.childs.length > 1 || (data && data.length == 1 && this.childs[0] && this.childs[0].attributes.getNamedItem('spry:suggest').value != this.getValue())) + { + this.showSuggestions(true); + } + else + return; + + var utils = Spry.Widget.Utils; + for (var k=0; k < this.childs.length; k++) + { + if (next) + { + utils.addClassName(this.childs[k], this.hoverSuggestClass); + this.scrollVisible(this.childs[k]); + break; + } + if (utils.hasClassName(this.childs[k], this.hoverSuggestClass)) + { + utils.removeClassName(this.childs[k], this.hoverSuggestClass); + found = true; + if (e.keyCode == this.moveNextKeyCode) + { + next = true; + continue; + } + else + { + utils.addClassName(this.childs[prev], this.hoverSuggestClass); + this.scrollVisible(this.childs[prev]); + break; + } + } + prev = k; + } + if (!found || (next && k == this.childs.length)) + { + utils.addClassName(this.childs[0], this.hoverSuggestClass); + this.scrollVisible(this.childs[0]); + } + utils.stopEvent(e); + break; + case 27: // ESC key + this.showSuggestions(false); + break; + case 13: //Enter Key + if (!this.isVisibleSuggestion()) + return; + for (var k=0; k < this.childs.length; k++) + if (Spry.Widget.Utils.hasClassName(this.childs[k], this.hoverSuggestClass)) + { + var attr = this.childs[k].attributes.getNamedItem('spry:suggest'); + if (attr){ + this.setValue(attr.value); + this.handleKeyUp(null); + } + // stop form submission + Spry.Widget.Utils.stopEvent(e); + return false; + } + break; + case 9: //Tab Key + this.showSuggestions(false); + } + return; +}; + +Spry.Widget.AutoSuggest.prototype.filterDataSet = function() +{ + var contains = this.containsString; + var columnName = this.field; + var val = this.getValue(); + + if (this.previousString && this.previousString == val) + return; + + this.previousString = val; + + if (!val || (this.minCharsType && this.minCharsType > val.length)) + { + this.dataset.filter(function(ds, row, rowNumber) {return null;}); + this.showSuggestions(false); + return; + } + + var regExpStr = Spry.Widget.Utils.escapeRegExp(val); + + if (!contains) + regExpStr = "^" + regExpStr; + + var regExp = new RegExp(regExpStr, "ig"); + + if (this.maxListItems > 0) + this.dataset.maxItems = this.maxListItems; + + var filterFunc = function(ds, row, rowNumber) + { + if (ds.maxItems >0 && ds.maxItems <= ds.data.length) + return null; + + if (typeof columnName == 'object') + { + var l = columnName.length; + for (var i=0; i < l; i++) + { + var str = row[columnName[i]]; + if (str && str.search(regExp) != -1) + return row; + } + } + else + { + var str = row[columnName]; + if (str && str.search(regExp) != -1) + return row; + } + return null; + }; + + this.dataset.filter(filterFunc); + var data = this.dataset.getData(); + this.showSuggestions(data && (!this.minCharsType || val.length >= this.minCharsType) && (data.length > 1 || (data.length == 1 && this.childs[0] && this.childs[0].attributes.getNamedItem('spry:suggest').value != val ))); +}; + +Spry.Widget.AutoSuggest.prototype.loadDataSet = function() +{ + var val = this.getValue(); + var ds = this.dataset; + ds.cancelLoadData(); + ds.useCache = false; + + if (!val || (this.minCharsType && this.minCharsType > val.length)) + { + this.showSuggestions(false); + return; + } + + if (this.previousString && this.previousString == val) + { + var data = ds.getData(); + this.showSuggestions(data && (data.length > 1 || (data.length == 1 && this.childs[0].attributes.getNamedItem("spry:suggest").value != val))); + return; + } + + this.previousString = val; + + var url = Spry.Widget.Utils.addReplaceParam(ds.url, this.urlParam, val); + ds.setURL(url); + ds.loadData(); +}; + +Spry.Widget.AutoSuggest.prototype.addMouseListener = function(node, value) +{ + var self = this; + var addListener = Spry.Widget.Utils.addEventListener; + addListener(node, "click", function(e){ return self.nodeClick(e, value); self.handleKeyUp(null);}, false); + addListener(node, "mouseover", function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); + addListener(node, "mouseout", function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); +}; +Spry.Widget.AutoSuggest.prototype.removeMouseListener = function(node, value) +{ + var self = this; + var removeListener = Spry.Widget.Utils.removeEventListener; + removeListener(node, "click", function (e){ self.nodeClick(e, value); self.handleKeyUp(null);}, false); + removeListener(node, "mouseover", function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); + removeListener(node, "mouseout", function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); +}; +Spry.Widget.AutoSuggest.prototype.attachClickBehaviors = function() +{ + var self = this; + var valNodes = Spry.Utils.getNodesByFunc(this.region, function(node) + { + if (node.nodeType == 1) /* Node.ELEMENT_NODE */ + { + var attr = node.attributes.getNamedItem("spry:suggest"); + if (attr){ + self.addMouseListener(node, attr.value); + return true; + } + } + return false; + }); + this.childs = valNodes; +}; +Spry.Widget.AutoSuggest.prototype.removeClickBehaviours = function() +{ + var self = this; + var valNodes = Spry.Utils.getNodesByFunc(this.region, function(node) + { + if (node.nodeType == 1) /* Node.ELEMENT_NODE */ + { + var attr = node.attributes.getNamedItem("spry:suggest"); + if (attr){ + self.removeMouseListener(node, attr.value); + return true; + } + } + return false; + }); +}; +Spry.Widget.AutoSuggest.prototype.destroy = function(){ + + this.removeClickBehaviours(); + Spry.Data.Region.removeObserver(Spry.Widget.Utils.getElementID(this.suggestRegion),this._notifyDataset); + + if (this.event_handlers) + for (var i=0; i0) + { + if(isFirstEntry) + { + camelizedString = oStringList[i]; + isFirstEntry = false; + } + else + { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + } + } + + return camelizedString; +}; + +Spry.Widget.Utils.getStyleProp = function(element, prop) +{ + var value; + var camel = Spry.Widget.Utils.camelize(prop); + try + { + value = element.style[camel]; + if (!value) + { + if (document.defaultView && document.defaultView.getComputedStyle) + { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(prop) : null; + } + else + if (element.currentStyle) + value = element.currentStyle[camel]; + } + } + catch (e) {} + + return value == 'auto' ? null : value; +}; +Spry.Widget.Utils.makePositioned = function(element) +{ + var pos = Spry.Widget.Utils.getStyleProp(element, 'position'); + if (!pos || pos == 'static') + { + element.style.position = 'relative'; + + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) + { + element.style.top = 0; + element.style.left = 0; + } + } +}; +Spry.Widget.Utils.escapeRegExp = function(rexp) +{ + return rexp.replace(/([\.\/\]\[\{\}\(\)\\\$\^\?\*\|\!\=\+\-])/g, '\\$1'); +}; +Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName) +{ + var elements = node.getElementsByTagName(nodeName); + if (elements) + return elements[0]; + + return null; +};