mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Autocompleter problems
o removed code duplication Issue Number: WW-1529 Submitted by: Musachy Barroso git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@481382 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -291,296 +291,3 @@ dojo.widget.defineWidget(
|
||||
}
|
||||
}
|
||||
});
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user