initial import from onelab svn codebase
[plewww.git] / planetlab / includes / js / bsn.AutoSuggest.js
1 /**
2  *  author:             Timothy Groves - http://www.brandspankingnew.net
3  *      version:        1.0 - 2006-08-03
4  *
5  *      requires:       bsn.DOM.js
6  *                              bsn.Ajax.js
7  *
8  */
9
10 var useBSNns;
11
12 if (useBSNns)
13 {
14         if (typeof(bsn) == "undefined")
15                 bsn = {}
16         _bsn = bsn;
17 }
18 else
19 {
20         _bsn = this;
21 }
22
23
24 if (typeof(_bsn.DOM) == "undefined")
25         _bsn.DOM = {}
26         
27
28
29
30
31
32
33
34 _bsn.AutoSuggest = function (fldID, param)
35 {
36         if (!document.getElementById)
37                 return false;
38         
39         this.fld = _bsn.DOM.getElement(fldID);
40
41         if (!this.fld)
42                 return false;
43                 
44                 
45         this.nInputChars = 0;
46         this.aSuggestions = [];
47         this.iHighlighted = 0;
48         
49         
50         // parameters object
51         this.oP = (param) ? param : {};
52         // defaults     
53         if (!this.oP.minchars)          this.oP.minchars = 1;
54         if (!this.oP.method)            this.oP.meth = "get";
55         if (!this.oP.varname)           this.oP.varname = "input";
56         if (!this.oP.className)         this.oP.className = "autosuggest";
57         if (!this.oP.timeout)           this.oP.timeout = 2500;
58         if (!this.oP.delay)                     this.oP.delay = 500;
59         if (!this.oP.maxheight && this.oP.maxheight !== 0)              this.oP.maxheight = 250;
60         if (!this.oP.cache)                     this.oP.cache = true;
61         
62         var pointer = this;
63         
64         this.fld.onkeyup = function () { pointer.getSuggestions( this.value ) };
65         this.fld.setAttribute("autocomplete","off");
66 }
67
68
69
70 _bsn.AutoSuggest.prototype.getSuggestions = function (val)
71 {
72
73         if (val.length == this.nInputChars)
74                 return false;
75         
76         if (val.length < this.oP.minchars)
77         {
78                 this.nInputChars = val.length;
79                 this.aSuggestions = [];
80                 this.clearSuggestions();
81                 return false;
82         }
83         
84         
85         if (val.length>this.nInputChars && this.aSuggestions.length && this.oP.cache)
86         {
87                 // get from cache
88                 var arr = [];
89                 for (var i=0;i<this.aSuggestions.length;i++)
90                 {
91                         if (this.aSuggestions[i].substr(0,val.length).toLowerCase() == val.toLowerCase())
92                                 arr.push( this.aSuggestions[i] );
93                 }
94                 
95                 this.nInputChars = val.length;
96                 this.aSuggestions = arr;
97                 
98                 
99                 this.createList( this.aSuggestions );
100                 
101                 return false;
102         }
103         
104         
105         this.nInputChars = val.length;
106         
107         var pointer = this;
108         clearTimeout(this.ajID);
109         this.ajID = setTimeout( function() { pointer.doAjaxRequest() }, this.oP.delay );
110
111
112         return false;
113 }
114
115
116
117
118
119 _bsn.AutoSuggest.prototype.doAjaxRequest = function ()
120 {
121         var pointer = this;
122         
123         // create ajax request
124         var url = this.oP.script+this.oP.varname+"="+this.fld.value;
125         var meth = this.oP.meth;
126         
127         var onSuccessFunc = function (req) { pointer.setSuggestions(req) };
128         var onErrorFunc = function (status) { alert("AJAX error: "+status); };
129
130         var myAjax = new _bsn.Ajax;
131         myAjax.makeRequest( url, meth, onSuccessFunc, onErrorFunc );
132 }
133
134
135
136
137
138 _bsn.AutoSuggest.prototype.setSuggestions = function (req)
139 {
140         
141         var xml = req.responseXML;
142         
143         // traverse xml
144         //
145         this.aSuggestions = [];
146         var results = xml.getElementsByTagName('results')[0].childNodes;
147
148         for (var i=0;i<results.length;i++)
149         {
150                 if (results[i].hasChildNodes())
151                         this.aSuggestions.push( results[i].childNodes[0].nodeValue );
152         }
153         
154         
155         this.idAs = "as_"+this.fld.id;
156         
157         
158         this.createList(this.aSuggestions);
159
160 }
161
162
163
164
165
166 _bsn.AutoSuggest.prototype.createList = function(arr)
167 {
168         // clear previous list
169         this.clearSuggestions();
170
171         // create and populate ul
172         var ul = _bsn.DOM.createElement("ul", {id:this.idAs, className:this.oP.className});
173         
174         
175         var pointer = this;
176         for (var i=0;i<arr.length;i++)
177         {
178                 var a = _bsn.DOM.createElement("a", { href:"#" }, arr[i]);
179                 a.onclick = function () { pointer.setValue( this.childNodes[0].nodeValue ); return false; }
180                 var li = _bsn.DOM.createElement(  "li", {className:this.oP.className}, a  );
181                 ul.appendChild(  li  );
182         }
183         
184         var pos = _bsn.DOM.getPos(this.fld);
185         
186         ul.style.left = pos.x + "px";
187         ul.style.top = ( pos.y + this.fld.offsetHeight ) + "px";
188         ul.style.width = this.fld.offsetWidth+"px";
189         ul.onmouseover = function(){ pointer.killTimeout() }
190         ul.onmouseout = function(){ pointer.resetTimeout() }
191
192
193         document.getElementsByTagName("body")[0].appendChild(ul);
194         
195         if (ul.offsetHeight > this.oP.maxheight && this.oP.maxheight != 0)
196         {
197                 ul.style['height'] = this.oP.maxheight;
198         }
199         
200         
201         var TAB = 9;
202         var ESC = 27;
203         var KEYUP = 38;
204         var KEYDN = 40;
205         var RETURN = 13;
206         
207         
208         
209         this.fld.onkeydown = function(ev)
210         {
211                 var key = (window.event) ? window.event.keyCode : ev.keyCode;
212
213                 switch(key)
214                 {
215                         case TAB:
216                         pointer.setHighlightedValue();
217                         break;
218
219                         case ESC:
220                         pointer.clearSuggestions();
221                         break;
222
223                         case KEYUP:
224                         pointer.changeHighlight(key);
225                         return false;
226                         break;
227
228                         case KEYDN:
229                         pointer.changeHighlight(key);
230                         return false;
231                         break;
232                 }
233
234         };
235
236         this.iHighlighted = 0;
237         
238         
239         // remove autosuggest after an interval
240         //
241         clearTimeout(this.toID);
242         var pointer = this;
243         this.toID = setTimeout(function () { pointer.clearSuggestions() }, this.oP.timeout);
244 }
245
246
247
248
249
250
251
252
253
254 _bsn.AutoSuggest.prototype.changeHighlight = function(key)
255 {
256         var list = _bsn.DOM.getElement(this.idAs);
257         if (!list)
258                 return false;
259         
260         
261         if (this.iHighlighted > 0)
262                 list.childNodes[this.iHighlighted-1].className = "";
263         
264         if (key == 40)
265                 this.iHighlighted ++;
266         else if (key = 38)
267                 this.iHighlighted --;
268         
269         
270         if (this.iHighlighted > list.childNodes.length)
271                 this.iHighlighted = list.childNodes.length;
272         if (this.iHighlighted < 1)
273                 this.iHighlighted = 1;
274         
275         list.childNodes[this.iHighlighted-1].className = "highlight";
276         
277         //alert( list.childNodes[this.iHighlighted-1].firstChild.firstChild.nodeValue );
278         
279         this.killTimeout();
280 }
281
282
283
284
285
286
287
288
289 _bsn.AutoSuggest.prototype.killTimeout = function()
290 {
291         clearTimeout(this.toID);
292 }
293
294 _bsn.AutoSuggest.prototype.resetTimeout = function()
295 {
296         clearTimeout(this.toID);
297         var pointer = this;
298         this.toID = setTimeout(function () { pointer.clearSuggestions() }, 1000);
299 }
300
301
302
303
304
305
306
307 _bsn.AutoSuggest.prototype.clearSuggestions = function ()
308 {
309         if (document.getElementById(this.idAs))
310                 _bsn.DOM.removeElement(this.idAs);
311         this.fld.onkeydown = null;
312 }
313
314
315
316
317
318
319
320 _bsn.AutoSuggest.prototype.setHighlightedValue = function ()
321 {
322         if (this.iHighlighted)
323         {
324                 this.fld.value = document.getElementById(this.idAs).childNodes[this.iHighlighted-1].firstChild.firstChild.nodeValue;
325                 this.killTimeout();
326                 this.clearSuggestions();
327         }
328 }
329
330
331
332 _bsn.AutoSuggest.prototype.setValue = function (val)
333 {
334         this.fld.value = val;
335         this.resetTimeout();
336 }