|
1 // SpryAutoSuggest.js - version 0.91 - Spry Pre-Release 1.6.1 |
|
2 // |
|
3 // Copyright (c) 2006. Adobe Systems Incorporated. |
|
4 // All rights reserved. |
|
5 // |
|
6 // Redistribution and use in source and binary forms, with or without |
|
7 // modification, are permitted provided that the following conditions are met: |
|
8 // |
|
9 // * Redistributions of source code must retain the above copyright notice, |
|
10 // this list of conditions and the following disclaimer. |
|
11 // * Redistributions in binary form must reproduce the above copyright notice, |
|
12 // this list of conditions and the following disclaimer in the documentation |
|
13 // and/or other materials provided with the distribution. |
|
14 // * Neither the name of Adobe Systems Incorporated nor the names of its |
|
15 // contributors may be used to endorse or promote products derived from this |
|
16 // software without specific prior written permission. |
|
17 // |
|
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
28 // POSSIBILITY OF SUCH DAMAGE. |
|
29 |
|
30 var Spry; |
|
31 if (!Spry) Spry = {}; |
|
32 if (!Spry.Widget) Spry.Widget = {}; |
|
33 |
|
34 Spry.Widget.BrowserSniff = function() |
|
35 { |
|
36 var b = navigator.appName.toString(); |
|
37 var up = navigator.platform.toString(); |
|
38 var ua = navigator.userAgent.toString(); |
|
39 |
|
40 this.mozilla = this.ie = this.opera = this.safari = false; |
|
41 var re_opera = /Opera.([0-9\.]*)/i; |
|
42 var re_msie = /MSIE.([0-9\.]*)/i; |
|
43 var re_gecko = /gecko/i; |
|
44 var re_safari = /(applewebkit|safari)\/([\d\.]*)/i; |
|
45 var r = false; |
|
46 |
|
47 if ( (r = ua.match(re_opera))) { |
|
48 this.opera = true; |
|
49 this.version = parseFloat(r[1]); |
|
50 } else if ( (r = ua.match(re_msie))) { |
|
51 this.ie = true; |
|
52 this.version = parseFloat(r[1]); |
|
53 } else if ( (r = ua.match(re_safari))) { |
|
54 this.safari = true; |
|
55 this.version = parseFloat(r[2]); |
|
56 } else if (ua.match(re_gecko)) { |
|
57 var re_gecko_version = /rv:\s*([0-9\.]+)/i; |
|
58 r = ua.match(re_gecko_version); |
|
59 this.mozilla = true; |
|
60 this.version = parseFloat(r[1]); |
|
61 } |
|
62 this.windows = this.mac = this.linux = false; |
|
63 |
|
64 this.Platform = ua.match(/windows/i) ? "windows" : |
|
65 (ua.match(/linux/i) ? "linux" : |
|
66 (ua.match(/mac/i) ? "mac" : |
|
67 ua.match(/unix/i)? "unix" : "unknown")); |
|
68 this[this.Platform] = true; |
|
69 this.v = this.version; |
|
70 |
|
71 if (this.safari && this.mac && this.mozilla) { |
|
72 this.mozilla = false; |
|
73 } |
|
74 }; |
|
75 |
|
76 Spry.is = new Spry.Widget.BrowserSniff(); |
|
77 |
|
78 Spry.Widget.AutoSuggest = function(region, sRegion, dataset, field, options) |
|
79 { |
|
80 if (!this.isBrowserSupported()) |
|
81 return; |
|
82 |
|
83 options = options || {}; |
|
84 |
|
85 this.init(region, sRegion, dataset, field); |
|
86 Spry.Widget.Utils.setOptions(this, options); |
|
87 |
|
88 if (Spry.Widget.AutoSuggest.onloadDidFire) |
|
89 this.attachBehaviors(); |
|
90 else |
|
91 Spry.Widget.AutoSuggest.loadQueue.push(this); |
|
92 |
|
93 // when data is changing we will decide if we will have to show the suggestions |
|
94 this.dataset.addObserver(this); |
|
95 |
|
96 // Set up an observer so we can attach our click behaviors whenever |
|
97 // the region is regenerated. |
|
98 var regionID = Spry.Widget.Utils.getElementID(sRegion); |
|
99 |
|
100 var self = this; |
|
101 this._notifyDataset = { onPostUpdate: function() { |
|
102 self.attachClickBehaviors(); |
|
103 }, onPreUpdate: function(){ |
|
104 self.removeClickBehaviours(); |
|
105 }}; |
|
106 |
|
107 Spry.Data.Region.addObserver(regionID,this._notifyDataset); |
|
108 |
|
109 // clean up the widget when on page unload |
|
110 Spry.Widget.Utils.addEventListener(window, 'unload', function(){self.destroy()}, false); |
|
111 |
|
112 // make the first computation in case the textfield is not empty |
|
113 this.attachClickBehaviors(); |
|
114 this.handleKeyUp(null); |
|
115 this.showSuggestions(false); |
|
116 }; |
|
117 |
|
118 Spry.Widget.AutoSuggest.prototype.init = function(region, sRegion, dataset, field) |
|
119 { |
|
120 this.region = Spry.Widget.Utils.getElement(region); |
|
121 |
|
122 if (!this.region) |
|
123 return; |
|
124 |
|
125 this.minCharsType = false; |
|
126 this.containsString = false; |
|
127 this.loadFromServer = false; |
|
128 this.urlParam = ''; |
|
129 this.suggestionIsVisible = false; |
|
130 this.stopFocus = false; |
|
131 this.hasFocus = false; |
|
132 this.showSuggestClass = 'showSuggestClass'; |
|
133 this.hideSuggestClass = 'hideSuggestClass'; |
|
134 this.hoverSuggestClass = 'hoverSuggestClass'; |
|
135 this.movePrevKeyCode = Spry.Widget.AutoSuggest.KEY_UP; |
|
136 this.moveNextKeyCode = Spry.Widget.AutoSuggest.KEY_DOWN; |
|
137 |
|
138 this.textElement = Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel(this.region, "INPUT"); |
|
139 this.textElement.setAttribute('AutoComplete', 'off'); |
|
140 |
|
141 this.suggestRegion = Spry.Widget.Utils.getElement(sRegion); |
|
142 // prepare the suggest region |
|
143 Spry.Widget.Utils.makePositioned(this.suggestRegion); |
|
144 Spry.Widget.Utils.addClassName(this.suggestRegion, this.hideSuggestClass); |
|
145 |
|
146 this.timerID = null; |
|
147 if (typeof dataset == "string"){ |
|
148 this.dataset = window[dataset]; |
|
149 }else{ |
|
150 this.dataset = dataset; |
|
151 } |
|
152 this.field = field; |
|
153 if (typeof field == 'string' && field.indexOf(',') != -1) |
|
154 { |
|
155 field = field.replace(/\s*,\s*/ig, ','); |
|
156 this.field = field.split(','); |
|
157 } |
|
158 }; |
|
159 |
|
160 Spry.Widget.AutoSuggest.prototype.isBrowserSupported = function() |
|
161 { |
|
162 return Spry.is.ie && Spry.is.v >= 5 && Spry.is.windows |
|
163 || |
|
164 Spry.is.mozilla && Spry.is.v >= 1.4 |
|
165 || |
|
166 Spry.is.safari |
|
167 || |
|
168 Spry.is.opera && Spry.is.v >= 9; |
|
169 }; |
|
170 |
|
171 Spry.Widget.AutoSuggest.prototype.getValue = function() |
|
172 { |
|
173 if (!this.textElement) |
|
174 return ''; |
|
175 return this.textElement.value; |
|
176 }; |
|
177 |
|
178 Spry.Widget.AutoSuggest.prototype.setValue = function(str) |
|
179 { |
|
180 if (!this.textElement) |
|
181 return; |
|
182 this.textElement.value = str; |
|
183 this.showSuggestions(false); |
|
184 }; |
|
185 |
|
186 Spry.Widget.AutoSuggest.prototype.focus = function() |
|
187 { |
|
188 if (!this.textElement) |
|
189 return; |
|
190 this.textElement.focus(); |
|
191 }; |
|
192 |
|
193 Spry.Widget.AutoSuggest.prototype.showSuggestions = function(doShow) |
|
194 { |
|
195 if (this.region && this.isVisibleSuggestion() != doShow) |
|
196 { |
|
197 if (doShow && this.hasFocus) |
|
198 { |
|
199 Spry.Widget.Utils.addClassName(this.region, this.showSuggestClass); |
|
200 if (Spry.is.ie && Spry.is.version < 7) |
|
201 this.createIframeLayer(this.suggestRegion); |
|
202 } |
|
203 else |
|
204 { |
|
205 if (Spry.is.ie && Spry.is.version < 7) |
|
206 this.removeIframeLayer(); |
|
207 Spry.Widget.Utils.removeClassName(this.region, this.showSuggestClass); |
|
208 } |
|
209 } |
|
210 this.suggestionIsVisible = Spry.Widget.Utils.hasClassName(this.region, this.showSuggestClass); |
|
211 }; |
|
212 |
|
213 Spry.Widget.AutoSuggest.prototype.isVisibleSuggestion = function() |
|
214 { |
|
215 return this.suggestionIsVisible; |
|
216 }; |
|
217 |
|
218 Spry.Widget.AutoSuggest.prototype.onDataChanged = function(el) |
|
219 { |
|
220 var data = el.getData(true); |
|
221 var val = this.getValue(); |
|
222 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()))); |
|
223 }; |
|
224 Spry.Widget.AutoSuggest.prototype.nodeMouseOver = function(e, node) |
|
225 { |
|
226 var l = this.childs.length; |
|
227 for (var i=0; i<l; i++) |
|
228 if (this.childs[i] != node && Spry.Widget.Utils.hasClassName(this.childs[i], this.hoverSuggestClass)) |
|
229 { |
|
230 Spry.Widget.Utils.removeClassName(this.childs[i], this.hoverSuggestClass); |
|
231 break; |
|
232 } |
|
233 }; |
|
234 Spry.Widget.AutoSuggest.prototype.nodeClick = function(e, value) |
|
235 { |
|
236 if (value) |
|
237 this.setValue(value); |
|
238 }; |
|
239 Spry.Widget.AutoSuggest.prototype.handleKeyUp = function(e) |
|
240 { |
|
241 if (this.timerID) |
|
242 { |
|
243 clearTimeout(this.timerID); |
|
244 this.timerID = null; |
|
245 } |
|
246 |
|
247 // If the user hit the escape key, hide the auto suggest menu! |
|
248 if (e && this.isSpecialKey(e)) |
|
249 { |
|
250 this.handleSpecialKeys(e); |
|
251 return; |
|
252 } |
|
253 |
|
254 var self = this; |
|
255 var func = function() { self.timerID = null; self.loadDataSet();}; |
|
256 if (!this.loadFromServer) |
|
257 func = function() { self.timerID = null; self.filterDataSet();}; |
|
258 |
|
259 this.timerID = setTimeout(func, 200); |
|
260 |
|
261 }; |
|
262 |
|
263 Spry.Widget.AutoSuggest.prototype.scrollVisible = function(el) |
|
264 { |
|
265 if (typeof this.scrolParent == 'undefined') |
|
266 { |
|
267 var currEl = el; |
|
268 this.scrolParent = false; |
|
269 while (!this.scrolParent) |
|
270 { |
|
271 var overflow = Spry.Widget.Utils.getStyleProp(currEl, 'overflow'); |
|
272 if (!overflow || overflow.toLowerCase() == 'scroll') |
|
273 { |
|
274 this.scrolParent = currEl; |
|
275 break; |
|
276 } |
|
277 if (currEl == this.region) |
|
278 break; |
|
279 |
|
280 currEl = currEl.parentNode; |
|
281 } |
|
282 } |
|
283 |
|
284 if (this.scrolParent != false) |
|
285 { |
|
286 var h = parseInt(Spry.Widget.Utils.getStyleProp(this.scrolParent, 'height'), 10); |
|
287 if (el.offsetTop < this.scrolParent.scrollTop) |
|
288 this.scrolParent.scrollTop = el.offsetTop; |
|
289 else if (el.offsetTop + el.offsetHeight > this.scrolParent.scrollTop + h) |
|
290 { |
|
291 // the 5 pixels make the latest option more visible. |
|
292 this.scrolParent.scrollTop = el.offsetTop + el.offsetHeight - h + 5; |
|
293 if (this.scrolParent.scrollTop < 0) |
|
294 this.scrolParent.scrollTop = 0; |
|
295 } |
|
296 |
|
297 } |
|
298 }; |
|
299 |
|
300 Spry.Widget.AutoSuggest.KEY_UP = 38; |
|
301 Spry.Widget.AutoSuggest.KEY_DOWN = 40; |
|
302 |
|
303 Spry.Widget.AutoSuggest.prototype.handleSpecialKeys = function(e){ |
|
304 |
|
305 switch (e.keyCode) |
|
306 { |
|
307 case this.moveNextKeyCode: // Down key |
|
308 case this.movePrevKeyCode: // Up Key |
|
309 if (!(this.childs.length > 0) || !this.getValue()) |
|
310 return; |
|
311 |
|
312 var prev = this.childs.length-1; |
|
313 var next = false; |
|
314 var found = false; |
|
315 var data = this.dataset.getData(); |
|
316 if (this.childs.length > 1 || (data && data.length == 1 && this.childs[0] && this.childs[0].attributes.getNamedItem('spry:suggest').value != this.getValue())) |
|
317 { |
|
318 this.showSuggestions(true); |
|
319 } |
|
320 else |
|
321 return; |
|
322 |
|
323 var utils = Spry.Widget.Utils; |
|
324 for (var k=0; k < this.childs.length; k++) |
|
325 { |
|
326 if (next) |
|
327 { |
|
328 utils.addClassName(this.childs[k], this.hoverSuggestClass); |
|
329 this.scrollVisible(this.childs[k]); |
|
330 break; |
|
331 } |
|
332 if (utils.hasClassName(this.childs[k], this.hoverSuggestClass)) |
|
333 { |
|
334 utils.removeClassName(this.childs[k], this.hoverSuggestClass); |
|
335 found = true; |
|
336 if (e.keyCode == this.moveNextKeyCode) |
|
337 { |
|
338 next = true; |
|
339 continue; |
|
340 } |
|
341 else |
|
342 { |
|
343 utils.addClassName(this.childs[prev], this.hoverSuggestClass); |
|
344 this.scrollVisible(this.childs[prev]); |
|
345 break; |
|
346 } |
|
347 } |
|
348 prev = k; |
|
349 } |
|
350 if (!found || (next && k == this.childs.length)) |
|
351 { |
|
352 utils.addClassName(this.childs[0], this.hoverSuggestClass); |
|
353 this.scrollVisible(this.childs[0]); |
|
354 } |
|
355 utils.stopEvent(e); |
|
356 break; |
|
357 case 27: // ESC key |
|
358 this.showSuggestions(false); |
|
359 break; |
|
360 case 13: //Enter Key |
|
361 if (!this.isVisibleSuggestion()) |
|
362 return; |
|
363 for (var k=0; k < this.childs.length; k++) |
|
364 if (Spry.Widget.Utils.hasClassName(this.childs[k], this.hoverSuggestClass)) |
|
365 { |
|
366 var attr = this.childs[k].attributes.getNamedItem('spry:suggest'); |
|
367 if (attr){ |
|
368 this.setValue(attr.value); |
|
369 this.handleKeyUp(null); |
|
370 } |
|
371 // stop form submission |
|
372 Spry.Widget.Utils.stopEvent(e); |
|
373 return false; |
|
374 } |
|
375 break; |
|
376 case 9: //Tab Key |
|
377 this.showSuggestions(false); |
|
378 } |
|
379 return; |
|
380 }; |
|
381 |
|
382 Spry.Widget.AutoSuggest.prototype.filterDataSet = function() |
|
383 { |
|
384 var contains = this.containsString; |
|
385 var columnName = this.field; |
|
386 var val = this.getValue(); |
|
387 |
|
388 if (this.previousString && this.previousString == val) |
|
389 return; |
|
390 |
|
391 this.previousString = val; |
|
392 |
|
393 if (!val || (this.minCharsType && this.minCharsType > val.length)) |
|
394 { |
|
395 this.dataset.filter(function(ds, row, rowNumber) {return null;}); |
|
396 this.showSuggestions(false); |
|
397 return; |
|
398 } |
|
399 |
|
400 var regExpStr = Spry.Widget.Utils.escapeRegExp(val); |
|
401 |
|
402 if (!contains) |
|
403 regExpStr = "^" + regExpStr; |
|
404 |
|
405 var regExp = new RegExp(regExpStr, "ig"); |
|
406 |
|
407 if (this.maxListItems > 0) |
|
408 this.dataset.maxItems = this.maxListItems; |
|
409 |
|
410 var filterFunc = function(ds, row, rowNumber) |
|
411 { |
|
412 if (ds.maxItems >0 && ds.maxItems <= ds.data.length) |
|
413 return null; |
|
414 |
|
415 if (typeof columnName == 'object') |
|
416 { |
|
417 var l = columnName.length; |
|
418 for (var i=0; i < l; i++) |
|
419 { |
|
420 var str = row[columnName[i]]; |
|
421 if (str && str.search(regExp) != -1) |
|
422 return row; |
|
423 } |
|
424 } |
|
425 else |
|
426 { |
|
427 var str = row[columnName]; |
|
428 if (str && str.search(regExp) != -1) |
|
429 return row; |
|
430 } |
|
431 return null; |
|
432 }; |
|
433 |
|
434 this.dataset.filter(filterFunc); |
|
435 var data = this.dataset.getData(); |
|
436 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 ))); |
|
437 }; |
|
438 |
|
439 Spry.Widget.AutoSuggest.prototype.loadDataSet = function() |
|
440 { |
|
441 var val = this.getValue(); |
|
442 var ds = this.dataset; |
|
443 ds.cancelLoadData(); |
|
444 ds.useCache = false; |
|
445 |
|
446 if (!val || (this.minCharsType && this.minCharsType > val.length)) |
|
447 { |
|
448 this.showSuggestions(false); |
|
449 return; |
|
450 } |
|
451 |
|
452 if (this.previousString && this.previousString == val) |
|
453 { |
|
454 var data = ds.getData(); |
|
455 this.showSuggestions(data && (data.length > 1 || (data.length == 1 && this.childs[0].attributes.getNamedItem("spry:suggest").value != val))); |
|
456 return; |
|
457 } |
|
458 |
|
459 this.previousString = val; |
|
460 |
|
461 var url = Spry.Widget.Utils.addReplaceParam(ds.url, this.urlParam, val); |
|
462 ds.setURL(url); |
|
463 ds.loadData(); |
|
464 }; |
|
465 |
|
466 Spry.Widget.AutoSuggest.prototype.addMouseListener = function(node, value) |
|
467 { |
|
468 var self = this; |
|
469 var addListener = Spry.Widget.Utils.addEventListener; |
|
470 addListener(node, "click", function(e){ return self.nodeClick(e, value); self.handleKeyUp(null);}, false); |
|
471 addListener(node, "mouseover", function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); |
|
472 addListener(node, "mouseout", function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); |
|
473 }; |
|
474 Spry.Widget.AutoSuggest.prototype.removeMouseListener = function(node, value) |
|
475 { |
|
476 var self = this; |
|
477 var removeListener = Spry.Widget.Utils.removeEventListener; |
|
478 removeListener(node, "click", function (e){ self.nodeClick(e, value); self.handleKeyUp(null);}, false); |
|
479 removeListener(node, "mouseover", function(e){ Spry.Widget.Utils.addClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); |
|
480 removeListener(node, "mouseout", function(e){ Spry.Widget.Utils.removeClassName(node, self.hoverSuggestClass); self.nodeMouseOver(e, node)}, false); |
|
481 }; |
|
482 Spry.Widget.AutoSuggest.prototype.attachClickBehaviors = function() |
|
483 { |
|
484 var self = this; |
|
485 var valNodes = Spry.Utils.getNodesByFunc(this.region, function(node) |
|
486 { |
|
487 if (node.nodeType == 1) /* Node.ELEMENT_NODE */ |
|
488 { |
|
489 var attr = node.attributes.getNamedItem("spry:suggest"); |
|
490 if (attr){ |
|
491 self.addMouseListener(node, attr.value); |
|
492 return true; |
|
493 } |
|
494 } |
|
495 return false; |
|
496 }); |
|
497 this.childs = valNodes; |
|
498 }; |
|
499 Spry.Widget.AutoSuggest.prototype.removeClickBehaviours = function() |
|
500 { |
|
501 var self = this; |
|
502 var valNodes = Spry.Utils.getNodesByFunc(this.region, function(node) |
|
503 { |
|
504 if (node.nodeType == 1) /* Node.ELEMENT_NODE */ |
|
505 { |
|
506 var attr = node.attributes.getNamedItem("spry:suggest"); |
|
507 if (attr){ |
|
508 self.removeMouseListener(node, attr.value); |
|
509 return true; |
|
510 } |
|
511 } |
|
512 return false; |
|
513 }); |
|
514 }; |
|
515 Spry.Widget.AutoSuggest.prototype.destroy = function(){ |
|
516 |
|
517 this.removeClickBehaviours(); |
|
518 Spry.Data.Region.removeObserver(Spry.Widget.Utils.getElementID(this.suggestRegion),this._notifyDataset); |
|
519 |
|
520 if (this.event_handlers) |
|
521 for (var i=0; i<this.event_handlers.length; i++) { |
|
522 Spry.Widget.Utils.removeEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false); |
|
523 } |
|
524 |
|
525 for (var k in this) |
|
526 { |
|
527 if (typeof this[k] != 'function'){ |
|
528 try { delete this[k]; } catch(err) {} |
|
529 } |
|
530 } |
|
531 }; |
|
532 |
|
533 Spry.Widget.AutoSuggest.onloadDidFire = false; |
|
534 Spry.Widget.AutoSuggest.loadQueue = []; |
|
535 |
|
536 Spry.Widget.AutoSuggest.processLoadQueue = function(handler) |
|
537 { |
|
538 Spry.Widget.AutoSuggest.onloadDidFire = true; |
|
539 var q = Spry.Widget.AutoSuggest.loadQueue; |
|
540 var qlen = q.length; |
|
541 for (var i = 0; i < qlen; i++) |
|
542 q[i].attachBehaviors(); |
|
543 }; |
|
544 |
|
545 Spry.Widget.AutoSuggest.addLoadListener = function(handler) |
|
546 { |
|
547 if (typeof window.addEventListener != 'undefined') |
|
548 window.addEventListener('load', handler, false); |
|
549 else if (typeof document.addEventListener != 'undefined') |
|
550 document.addEventListener('load', handler, false); |
|
551 else if (typeof window.attachEvent != 'undefined') |
|
552 window.attachEvent('onload', handler); |
|
553 }; |
|
554 |
|
555 Spry.Widget.AutoSuggest.addLoadListener(Spry.Widget.AutoSuggest.processLoadQueue); |
|
556 |
|
557 Spry.Widget.AutoSuggest.prototype.attachBehaviors = function() |
|
558 { |
|
559 this.event_handlers = []; |
|
560 var self = this; |
|
561 // adding listeners to the text input to catch the text changes |
|
562 var _notifyKeyUp = function(e){ self.handleKeyUp(e)}; |
|
563 this.event_handlers.push([this.textElement, "keydown", _notifyKeyUp]); |
|
564 this.event_handlers.push([this.textElement, "focus", function(e){ if (self.stopFocus){ self.handleKeyUp(e);} self.hasFocus = true; self.stopFocus = false;}]); |
|
565 this.event_handlers.push([this.textElement, "drop", _notifyKeyUp]); |
|
566 this.event_handlers.push([this.textElement, "dragdrop", _notifyKeyUp]); |
|
567 |
|
568 var _notifyBlur = false; |
|
569 // on opera the blur is triggered before onclick |
|
570 if (Spry.is.opera){ |
|
571 _notifyBlur = function(e) { setTimeout(function(){if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;}, 100); }; |
|
572 }else{ |
|
573 _notifyBlur = function(e) { if (!self.clickInList){ self.showSuggestions(false); }else{ self.stopFocus = true; self.textElement.focus();} self.clickInList = false; self.hasFocus = false;}; |
|
574 } |
|
575 this.event_handlers.push([this.textElement, "blur", _notifyBlur]); |
|
576 |
|
577 // we listen on the suggest region too |
|
578 this.event_handlers.push([this.suggestRegion, "mousedown", function(e){ self.clickInList = true;}]); |
|
579 |
|
580 for (var i=0; i<this.event_handlers.length; i++) |
|
581 Spry.Widget.Utils.addEventListener(this.event_handlers[i][0], this.event_handlers[i][1], this.event_handlers[i][2], false); |
|
582 }; |
|
583 |
|
584 // createIframeLayer for Tooltip |
|
585 // creates an IFRAME underneath a tooltip element so that it will show above form controls and ActiveX |
|
586 Spry.Widget.AutoSuggest.prototype.createIframeLayer = function(element) |
|
587 { |
|
588 if (typeof this.iframeLayer == 'undefined') |
|
589 { |
|
590 var layer = document.createElement('iframe'); |
|
591 layer.tabIndex = '-1'; |
|
592 layer.src = 'javascript:"";'; |
|
593 layer.scrolling = 'no'; |
|
594 layer.frameBorder = '0'; |
|
595 layer.className = 'iframeSuggest'; |
|
596 element.parentNode.appendChild(layer); |
|
597 this.iframeLayer = layer; |
|
598 } |
|
599 this.iframeLayer.style.left = element.offsetLeft + 'px'; |
|
600 this.iframeLayer.style.top = element.offsetTop + 'px'; |
|
601 this.iframeLayer.style.width = element.offsetWidth + 'px'; |
|
602 this.iframeLayer.style.height = element.offsetHeight + 'px'; |
|
603 this.iframeLayer.style.display = 'block'; |
|
604 }; |
|
605 |
|
606 // removeIframeLayer for Tooltip Element |
|
607 // removes an IFRAME underneath a tooltip to reveal any form controls and ActiveX |
|
608 Spry.Widget.AutoSuggest.prototype.removeIframeLayer = function() |
|
609 { |
|
610 if (this.iframeLayer) |
|
611 this.iframeLayer.style.display = 'none'; |
|
612 }; |
|
613 |
|
614 ////////////////////////////////////////////////////////////////////// |
|
615 // |
|
616 // Spry.Widget.Utils |
|
617 // |
|
618 ////////////////////////////////////////////////////////////////////// |
|
619 if (!Spry.Widget.Utils) Spry.Widget.Utils = {}; |
|
620 |
|
621 Spry.Widget.Utils.specialSafariNavKeys = ",63232,63233,63234,63235,63272,63273,63275,63276,63277,63289,"; //left,up,rigtht,down arrows,delete,home,end,page up,page down,num lock |
|
622 Spry.Widget.Utils.specialCharacters = ",9,13,27,38,40,"; //suggest keys: tab,enter,escape,up arrow,down arrow |
|
623 Spry.Widget.Utils.specialCharacters += ",33,34,35,36,37,39,45,46,"; //edit keys: insert,delete,home,end,left arrow,right arrow,page up,page down |
|
624 Spry.Widget.Utils.specialCharacters += ",16,17,18,19,20,144,145,"; //control keys: shift,control,alt,pause,caps lock,num lock,scroll lock |
|
625 Spry.Widget.Utils.specialCharacters += ",112,113,114,115,116,117,118,119,120,121,122,123,"; //F1-F12 |
|
626 Spry.Widget.Utils.specialCharacters += Spry.Widget.Utils.specialSafariNavKeys; |
|
627 |
|
628 Spry.Widget.AutoSuggest.prototype.isSpecialKey = function (ev) |
|
629 { |
|
630 return Spry.Widget.Utils.specialCharacters.indexOf("," + ev.keyCode + ",") != -1 || this.moveNextKeyCode == ev.keyCode || this.movePrevKeyCode == ev.keyCode; |
|
631 }; |
|
632 Spry.Widget.Utils.getElementID = function(el) |
|
633 { |
|
634 if (typeof el == 'string' && el) |
|
635 return el; |
|
636 return el.getAttribute('id'); |
|
637 }; |
|
638 Spry.Widget.Utils.getElement = function(ele) |
|
639 { |
|
640 if (ele && typeof ele == "string") |
|
641 return document.getElementById(ele); |
|
642 return ele; |
|
643 }; |
|
644 Spry.Widget.Utils.addReplaceParam = function(url, param, paramValue) |
|
645 { |
|
646 var uri =''; |
|
647 var qstring = ''; |
|
648 var i = url.indexOf('?'); |
|
649 if ( i != -1) |
|
650 { |
|
651 uri = url.slice(0, i); |
|
652 qstring = url.slice(i+1); |
|
653 } |
|
654 else |
|
655 uri = url; |
|
656 |
|
657 // the list of parameters |
|
658 qstring = qstring.replace('?', ''); |
|
659 var arg = qstring.split("&"); |
|
660 |
|
661 // prevent malicious use |
|
662 if (param.lastIndexOf('/') != -1) |
|
663 param = param.slice(param.lastIndexOf('/')+1); |
|
664 |
|
665 // remove param from the list |
|
666 for (i=0; i < arg.length ;i++) |
|
667 { |
|
668 var k = arg[i].split('='); |
|
669 if ( (k[0] && k[0] == decodeURI(param)) || arg[i] == decodeURI(param)) |
|
670 arg[i] = null; |
|
671 } |
|
672 |
|
673 arg[arg.length] = encodeURIComponent(param) + '=' + encodeURIComponent(paramValue); |
|
674 qstring = ''; |
|
675 // reconstruct the qstring |
|
676 for (i=0; i < arg.length; i++) |
|
677 if (arg[i]) |
|
678 qstring += '&'+arg[i]; |
|
679 |
|
680 // remove the first & |
|
681 qstring = qstring.slice(1); |
|
682 |
|
683 url = uri + '?' + qstring; |
|
684 return url; |
|
685 }; |
|
686 |
|
687 Spry.Widget.Utils.addClassName = function(ele, clssName) |
|
688 { |
|
689 if (!ele) return; |
|
690 |
|
691 if (!ele.className) ele.className = ''; |
|
692 |
|
693 if (!ele || ele.className.search(new RegExp("\\b" + clssName + "\\b")) != -1) |
|
694 return; |
|
695 |
|
696 ele.className += ' ' + clssName; |
|
697 }; |
|
698 |
|
699 Spry.Widget.Utils.removeClassName = function(ele, className) |
|
700 { |
|
701 if (!ele) return; |
|
702 |
|
703 if (!ele.className) |
|
704 { |
|
705 ele.className = ''; |
|
706 return; |
|
707 } |
|
708 ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), ''); |
|
709 }; |
|
710 |
|
711 Spry.Widget.Utils.hasClassName = function (ele, className) |
|
712 { |
|
713 if (!ele || !className) |
|
714 return false; |
|
715 |
|
716 if (!ele.className) |
|
717 ele.className = ''; |
|
718 |
|
719 return ele.className.search(new RegExp("\\s*\\b" + className + "\\b")) != -1; |
|
720 }; |
|
721 |
|
722 Spry.Widget.Utils.addEventListener = function(el, eventType, handler, capture) |
|
723 { |
|
724 try |
|
725 { |
|
726 if (el.addEventListener) |
|
727 el.addEventListener(eventType, handler, capture); |
|
728 else if (el.attachEvent) |
|
729 el.attachEvent("on" + eventType, handler, capture); |
|
730 }catch (e) {} |
|
731 }; |
|
732 |
|
733 Spry.Widget.Utils.removeEventListener = function(el, eventType, handler, capture) |
|
734 { |
|
735 try |
|
736 { |
|
737 if (el.removeEventListener) |
|
738 el.removeEventListener(eventType, handler, capture); |
|
739 else if (el.detachEvent) |
|
740 el.detachEvent("on" + eventType, handler, capture); |
|
741 }catch (e) {} |
|
742 }; |
|
743 |
|
744 Spry.Widget.Utils.stopEvent = function(ev) |
|
745 { |
|
746 ev.cancelBubble = true; |
|
747 ev.returnValue = false; |
|
748 |
|
749 try |
|
750 { |
|
751 this.stopPropagation(ev); |
|
752 }catch (e){} |
|
753 try{ |
|
754 this.preventDefault(ev); |
|
755 }catch(e){} |
|
756 }; |
|
757 |
|
758 /** |
|
759 * Stops event propagation |
|
760 * @param {Event} ev the event |
|
761 */ |
|
762 Spry.Widget.Utils.stopPropagation = function(ev) |
|
763 { |
|
764 if (ev.stopPropagation) |
|
765 ev.stopPropagation(); |
|
766 else |
|
767 ev.cancelBubble = true; |
|
768 }; |
|
769 |
|
770 /** |
|
771 * Prevents the default behavior of the event |
|
772 * @param {Event} ev the event |
|
773 */ |
|
774 Spry.Widget.Utils.preventDefault = function(ev) |
|
775 { |
|
776 if (ev.preventDefault) |
|
777 ev.preventDefault(); |
|
778 else |
|
779 ev.returnValue = false; |
|
780 }; |
|
781 |
|
782 Spry.Widget.Utils.setOptions = function(obj, optionsObj, ignoreUndefinedProps) |
|
783 { |
|
784 if (!optionsObj) |
|
785 return; |
|
786 for (var optionName in optionsObj) |
|
787 { |
|
788 if (typeof ignoreUndefinedProps != 'undefined' && ignoreUndefinedProps && typeof optionsObj[optionName] == 'undefined') |
|
789 continue; |
|
790 obj[optionName] = optionsObj[optionName]; |
|
791 } |
|
792 }; |
|
793 |
|
794 Spry.Widget.Utils.firstValid = function() |
|
795 { |
|
796 var ret = null; |
|
797 for (var i=0; i < Spry.Widget.Utils.firstValid.arguments.length; i++) |
|
798 if (typeof Spry.Widget.Utils.firstValid.arguments[i] != 'undefined') |
|
799 { |
|
800 ret = Spry.Widget.Utils.firstValid.arguments[i]; |
|
801 break; |
|
802 } |
|
803 |
|
804 return ret; |
|
805 }; |
|
806 |
|
807 Spry.Widget.Utils.camelize = function(stringToCamelize) |
|
808 { |
|
809 var oStringList = stringToCamelize.split('-'); |
|
810 var isFirstEntry = true; |
|
811 var camelizedString = ''; |
|
812 |
|
813 for(var i=0; i < oStringList.length; i++) |
|
814 { |
|
815 if(oStringList[i].length>0) |
|
816 { |
|
817 if(isFirstEntry) |
|
818 { |
|
819 camelizedString = oStringList[i]; |
|
820 isFirstEntry = false; |
|
821 } |
|
822 else |
|
823 { |
|
824 var s = oStringList[i]; |
|
825 camelizedString += s.charAt(0).toUpperCase() + s.substring(1); |
|
826 } |
|
827 } |
|
828 } |
|
829 |
|
830 return camelizedString; |
|
831 }; |
|
832 |
|
833 Spry.Widget.Utils.getStyleProp = function(element, prop) |
|
834 { |
|
835 var value; |
|
836 var camel = Spry.Widget.Utils.camelize(prop); |
|
837 try |
|
838 { |
|
839 value = element.style[camel]; |
|
840 if (!value) |
|
841 { |
|
842 if (document.defaultView && document.defaultView.getComputedStyle) |
|
843 { |
|
844 var css = document.defaultView.getComputedStyle(element, null); |
|
845 value = css ? css.getPropertyValue(prop) : null; |
|
846 } |
|
847 else |
|
848 if (element.currentStyle) |
|
849 value = element.currentStyle[camel]; |
|
850 } |
|
851 } |
|
852 catch (e) {} |
|
853 |
|
854 return value == 'auto' ? null : value; |
|
855 }; |
|
856 Spry.Widget.Utils.makePositioned = function(element) |
|
857 { |
|
858 var pos = Spry.Widget.Utils.getStyleProp(element, 'position'); |
|
859 if (!pos || pos == 'static') |
|
860 { |
|
861 element.style.position = 'relative'; |
|
862 |
|
863 // Opera returns the offset relative to the positioning context, when an |
|
864 // element is position relative but top and left have not been defined |
|
865 if (window.opera) |
|
866 { |
|
867 element.style.top = 0; |
|
868 element.style.left = 0; |
|
869 } |
|
870 } |
|
871 }; |
|
872 Spry.Widget.Utils.escapeRegExp = function(rexp) |
|
873 { |
|
874 return rexp.replace(/([\.\/\]\[\{\}\(\)\\\$\^\?\*\|\!\=\+\-])/g, '\\$1'); |
|
875 }; |
|
876 Spry.Widget.Utils.getFirstChildWithNodeNameAtAnyLevel = function(node, nodeName) |
|
877 { |
|
878 var elements = node.getElementsByTagName(nodeName); |
|
879 if (elements) |
|
880 return elements[0]; |
|
881 |
|
882 return null; |
|
883 }; |