Author: hermanns Date: Fri Dec 1 12:54:52 2006 New Revision: 481382 URL: http://svn.apache.org/viewvc?view=rev&rev=481382 Log: Autocompleter problems o removed code duplication
Issue Number: WW-1529 Submitted by: Musachy Barroso Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/struts/widget/ComboBox.js Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/struts/widget/ComboBox.js URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/struts/widget/ComboBox.js?view=diff&rev=481382&r1=481381&r2=481382 ============================================================================== --- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/struts/widget/ComboBox.js (original) +++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/struts/widget/ComboBox.js Fri Dec 1 12:54:52 2006 @@ -291,296 +291,3 @@ } } }); -dojo.provide("struts.widget.ComboBox"); - -dojo.require("dojo.html.*"); -dojo.require("dojo.widget.ComboBox"); - -struts.widget.ComboBoxDataProvider = function(/*Array*/ dataPairs, /*Number*/ limit, /*Number*/ timeout){ - // NOTE: this data provider is designed as a naive reference - // implementation, and as such it is written more for readability than - // speed. A deployable data provider would implement lookups, search - // caching (and invalidation), and a significantly less naive data - // structure for storage of items. - - this.data = []; - this.searchTimeout = timeout || 500; - this.searchLimit = limit || 30; - this.searchType = "STARTSTRING"; // may also be "STARTWORD" or "SUBSTRING" - this.caseSensitive = false; - // for caching optimizations - this._lastSearch = ""; - this._lastSearchResults = null; - - this.beforeLoading = ""; - this.afterLoading = ""; - - this.formId = ""; - this.formFilter = ""; - - this.init = function(/*Widget*/ cbox, /*DomNode*/ node){ - this.beforeLoading = cbox.beforeLoading; - this.afterLoading = cbox.afterLoading; - - this.formId = cbox.formId; - this.formFilter = cbox.formFilter; - - if(!dojo.string.isBlank(cbox.dataUrl)){ - this.getData(cbox.dataUrl); - }else{ - // check to see if we can populate the list from <option> elements - if((node)&&(node.nodeName.toLowerCase() == "select")){ - // NOTE: we're not handling <optgroup> here yet - var opts = node.getElementsByTagName("option"); - var ol = opts.length; - var data = []; - for(var x=0; x<ol; x++){ - var text = opts[x].textContent || opts[x].innerText || opts[x].innerHTML; - var keyValArr = [String(text), String(opts[x].value)]; - data.push(keyValArr); - if(opts[x].selected){ - cbox.setAllValues(keyValArr[0], keyValArr[1]); - } - } - this.setData(data); - } - } - }; - - this.getData = function(/*String*/ url){ - if(!dojo.string.isBlank(this.beforeLoading)) { - eval(this.beforeLoading); - } - - dojo.io.bind({ - url: url, - formNode: dojo.byId(this.formId), - formFilter: window[this.formFilter], - load: dojo.lang.hitch(this, function(type, data, evt){ - if(!dojo.string.isBlank(this.afterLoading)) { - eval(this.afterLoading); - } - if(!dojo.lang.isArray(data)){ - var arrData = []; - for(var key in data){ - arrData.push([data[key], key]); - } - data = arrData; - } - this.setData(data); - }), - mimetype: "text/json" - }); - }; - - this.startSearch = function(/*String*/ searchStr, /*String*/ type, /*Boolean*/ ignoreLimit){ - // FIXME: need to add timeout handling here!! - this._preformSearch(searchStr, type, ignoreLimit); - }; - - this._preformSearch = function(/*String*/ searchStr, /*String*/ type, /*Boolean*/ ignoreLimit){ - // - // NOTE: this search is LINEAR, which means that it exhibits perhaps - // the worst possible speed characteristics of any search type. It's - // written this way to outline the responsibilities and interfaces for - // a search. - // - var st = type||this.searchType; - // FIXME: this is just an example search, which means that we implement - // only a linear search without any of the attendant (useful!) optimizations - var ret = []; - if(!this.caseSensitive){ - searchStr = searchStr.toLowerCase(); - } - for(var x=0; x<this.data.length; x++){ - if((!ignoreLimit)&&(ret.length >= this.searchLimit)){ - break; - } - // FIXME: we should avoid copies if possible! - var dataLabel = new String((!this.caseSensitive) ? this.data[x][0].toLowerCase() : this.data[x][0]); - if(dataLabel.length < searchStr.length){ - // this won't ever be a good search, will it? What if we start - // to support regex search? - continue; - } - - if(st == "STARTSTRING"){ - if(searchStr == dataLabel.substr(0, searchStr.length)){ - ret.push(this.data[x]); - } - }else if(st == "SUBSTRING"){ - // this one is a gimmie - if(dataLabel.indexOf(searchStr) >= 0){ - ret.push(this.data[x]); - } - }else if(st == "STARTWORD"){ - // do a substring search and then attempt to determine if the - // preceeding char was the beginning of the string or a - // whitespace char. - var idx = dataLabel.indexOf(searchStr); - if(idx == 0){ - // implicit match - ret.push(this.data[x]); - } - if(idx <= 0){ - // if we didn't match or implicily matched, march onward - continue; - } - // otherwise, we have to go figure out if the match was at the - // start of a word... - // this code is taken almost directy from nWidgets - var matches = false; - while(idx!=-1){ - // make sure the match either starts whole string, or - // follows a space, or follows some punctuation - if(" ,/(".indexOf(dataLabel.charAt(idx-1)) != -1){ - // FIXME: what about tab chars? - matches = true; break; - } - idx = dataLabel.indexOf(searchStr, idx+1); - } - if(!matches){ - continue; - }else{ - ret.push(this.data[x]); - } - } - } - this.provideSearchResults(ret); - }; - - this.provideSearchResults = function(/*Array*/ resultsDataPairs){ - }; - - this.addData = function(/*Array*/ pairs){ - // FIXME: incredibly naive and slow! - this.data = this.data.concat(pairs); - }; - - this.setData = function(/*Array*/ pdata){ - // populate this.data and initialize lookup structures - this.data = pdata; - }; - - if(dataPairs){ - this.setData(dataPairs); - } -}; - -dojo.widget.defineWidget( - "struts.widget.ComboBox", - dojo.widget.ComboBox, { - widgetType : "ComboBox", - - dropdownHeight: 120, - dropdownWidth: 0, - itemHeight: 0, - - refreshListenTopic : "", - onValueChangedPublishTopic : "", - - //callbacks - beforeLoading : "", - afterLoading : "", - - formId : "", - formFilter : "", - dataProviderClass: "struts.widget.ComboBoxDataProvider", - //from Dojo's ComboBox - showResultList: function() { - // Our dear friend IE doesnt take max-height so we need to calculate that on our own every time - var childs = this.optionsListNode.childNodes; - if(childs.length){ - - this.optionsListNode.style.width = this.dropdownWidth === 0 ? (dojo.html.getMarginBox(this.domNode).width-2)+"px" : this.dropdownWidth + "px"; - - if(this.itemHeight === 0 || dojo.string.isBlank(this.textInputNode.value)) { - this.optionsListNode.style.height = this.dropdownHeight + "px"; - this.optionsListNode.style.display = ""; - this.itemHeight = dojo.html.getMarginBox(childs[0]).height; - } - - //if there is extra space, adjust height - var totalHeight = this.itemHeight * childs.length; - if(totalHeight < this.dropdownHeight) { - this.optionsListNode.style.height = totalHeight + 2 + "px"; - } - - this.popupWidget.open(this.domNode, this, this.downArrowNode); - } else { - this.hideResultList(); - } - }, - - openResultList: function(/*Array*/ results){ - if (!this.isEnabled){ - return; - } - this.clearResultList(); - if(!results.length){ - this.hideResultList(); - } - - if( (this.autoComplete)&& - (results.length)&& - (!this._prev_key_backspace)&& - (this.textInputNode.value.length > 0)){ - var cpos = this.getCaretPos(this.textInputNode); - // only try to extend if we added the last character at the end of the input - if((cpos+1) > this.textInputNode.value.length){ - // only add to input node as we would overwrite Capitalisation of chars - this.textInputNode.value += results[0][0].substr(cpos); - // build a new range that has the distance from the earlier - // caret position to the end of the first string selected - this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length); - } - } - var typedText = this.textInputNode.value; - var even = true; - while(results.length){ - var tr = results.shift(); - if(tr){ - var td = document.createElement("div"); - var text = tr[0]; - var i = text.toLowerCase().indexOf(typedText.toLowerCase()); - if(i >= 0) { - var pre = text.substring(0, i); - var matched = text.substring(i, typedText.length); - var post = text.substring(i + typedText.length); - - td.appendChild(document.createTextNode(pre)); - var boldNode = document.createElement("b"); - td.appendChild(boldNode); - boldNode.appendChild(document.createTextNode(matched)); - td.appendChild(document.createTextNode(post)); - } else { - td.appendChild(document.createTextNode(tr[0])); - } - - td.setAttribute("resultName", tr[0]); - td.setAttribute("resultValue", tr[1]); - td.className = "dojoComboBoxItem "+((even) ? "dojoComboBoxItemEven" : "dojoComboBoxItemOdd"); - even = (!even); - this.optionsListNode.appendChild(td); - } - } - - // show our list (only if we have content, else nothing) - this.showResultList(); - }, - - postCreate : function() { - struts.widget.ComboBox.superclass.postCreate.apply(this); - - //events - if(!dojo.string.isBlank(this.refreshListenTopic)) { - var self = this; - dojo.event.topic.subscribe(this.refreshListenTopic, function() { - self.dataProvider.getData(self.dataUrl); - }); - } - if(!dojo.string.isBlank(this.onValueChangedPublishTopic)) { - dojo.event.topic.registerPublisher(this.onValueChangedPublishTopic, this, "onValueChanged"); - } - } -});