allow string based values for comboboxarray
[roojs1] / Roo / lib / Event.js
1 /*
2  * Portions of this file are based on pieces of Yahoo User Interface Library
3  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4  * YUI licensed under the BSD License:
5  * http://developer.yahoo.net/yui/license.txt
6  * <script type="text/javascript">
7  *
8  */
9
10 Roo.lib.Event = function() {
11     var loadComplete = false;
12     var listeners = [];
13     var unloadListeners = [];
14     var retryCount = 0;
15     var onAvailStack = [];
16     var counter = 0;
17     var lastError = null;
18
19     return {
20         POLL_RETRYS: 200,
21         POLL_INTERVAL: 20,
22         EL: 0,
23         TYPE: 1,
24         FN: 2,
25         WFN: 3,
26         OBJ: 3,
27         ADJ_SCOPE: 4,
28         _interval: null,
29
30         startInterval: function() {
31             if (!this._interval) {
32                 var self = this;
33                 var callback = function() {
34                     self._tryPreloadAttach();
35                 };
36                 this._interval = setInterval(callback, this.POLL_INTERVAL);
37
38             }
39         },
40
41         onAvailable: function(p_id, p_fn, p_obj, p_override) {
42             onAvailStack.push({ id:         p_id,
43                 fn:         p_fn,
44                 obj:        p_obj,
45                 override:   p_override,
46                 checkReady: false    });
47
48             retryCount = this.POLL_RETRYS;
49             this.startInterval();
50         },
51
52
53         addListener: function(el, eventName, fn) {
54             el = Roo.getDom(el);
55             if (!el || !fn) {
56                 return false;
57             }
58
59             if ("unload" == eventName) {
60                 unloadListeners[unloadListeners.length] =
61                 [el, eventName, fn];
62                 return true;
63             }
64
65             var wrappedFn = function(e) {
66                 return fn(Roo.lib.Event.getEvent(e));
67             };
68
69             var li = [el, eventName, fn, wrappedFn];
70
71             var index = listeners.length;
72             listeners[index] = li;
73
74             this.doAdd(el, eventName, wrappedFn, false);
75             return true;
76
77         },
78
79
80         removeListener: function(el, eventName, fn) {
81             var i, len;
82
83             el = Roo.getDom(el);
84
85             if(!fn) {
86                 return this.purgeElement(el, false, eventName);
87             }
88
89
90             if ("unload" == eventName) {
91
92                 for (i = 0,len = unloadListeners.length; i < len; i++) {
93                     var li = unloadListeners[i];
94                     if (li &&
95                         li[0] == el &&
96                         li[1] == eventName &&
97                         li[2] == fn) {
98                         unloadListeners.splice(i, 1);
99                         return true;
100                     }
101                 }
102
103                 return false;
104             }
105
106             var cacheItem = null;
107
108
109             var index = arguments[3];
110
111             if ("undefined" == typeof index) {
112                 index = this._getCacheIndex(el, eventName, fn);
113             }
114
115             if (index >= 0) {
116                 cacheItem = listeners[index];
117             }
118
119             if (!el || !cacheItem) {
120                 return false;
121             }
122
123             this.doRemove(el, eventName, cacheItem[this.WFN], false);
124
125             delete listeners[index][this.WFN];
126             delete listeners[index][this.FN];
127             listeners.splice(index, 1);
128
129             return true;
130
131         },
132
133
134         getTarget: function(ev, resolveTextNode) {
135             ev = ev.browserEvent || ev;
136             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
137             var t = ev.target || ev.srcElement;
138             return this.resolveTextNode(t);
139         },
140
141
142         resolveTextNode: function(node) {
143             if (Roo.isSafari && node && 3 == node.nodeType) {
144                 return node.parentNode;
145             } else {
146                 return node;
147             }
148         },
149
150
151         getPageX: function(ev) {
152             ev = ev.browserEvent || ev;
153             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
154             var x = ev.pageX;
155             if (!x && 0 !== x) {
156                 x = ev.clientX || 0;
157
158                 if (Roo.isIE) {
159                     x += this.getScroll()[1];
160                 }
161             }
162
163             return x;
164         },
165
166
167         getPageY: function(ev) {
168             ev = ev.browserEvent || ev;
169             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
170             var y = ev.pageY;
171             if (!y && 0 !== y) {
172                 y = ev.clientY || 0;
173
174                 if (Roo.isIE) {
175                     y += this.getScroll()[0];
176                 }
177             }
178
179
180             return y;
181         },
182
183
184         getXY: function(ev) {
185             ev = ev.browserEvent || ev;
186             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
187             return [this.getPageX(ev), this.getPageY(ev)];
188         },
189
190
191         getRelatedTarget: function(ev) {
192             ev = ev.browserEvent || ev;
193             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
194             var t = ev.relatedTarget;
195             if (!t) {
196                 if (ev.type == "mouseout") {
197                     t = ev.toElement;
198                 } else if (ev.type == "mouseover") {
199                     t = ev.fromElement;
200                 }
201             }
202
203             return this.resolveTextNode(t);
204         },
205
206
207         getTime: function(ev) {
208             ev = ev.browserEvent || ev;
209             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
210             if (!ev.time) {
211                 var t = new Date().getTime();
212                 try {
213                     ev.time = t;
214                 } catch(ex) {
215                     this.lastError = ex;
216                     return t;
217                 }
218             }
219
220             return ev.time;
221         },
222
223
224         stopEvent: function(ev) {
225             this.stopPropagation(ev);
226             this.preventDefault(ev);
227         },
228
229
230         stopPropagation: function(ev) {
231             ev = ev.browserEvent || ev;
232             if (ev.stopPropagation) {
233                 ev.stopPropagation();
234             } else {
235                 ev.cancelBubble = true;
236             }
237         },
238
239
240         preventDefault: function(ev) {
241             ev = ev.browserEvent || ev;
242             if(ev.preventDefault) {
243                 ev.preventDefault();
244             } else {
245                 ev.returnValue = false;
246             }
247         },
248
249
250         getEvent: function(e) {
251             var ev = e || window.event;
252             if (!ev) {
253                 var c = this.getEvent.caller;
254                 while (c) {
255                     ev = c.arguments[0];
256                     if (ev && Event == ev.constructor) {
257                         break;
258                     }
259                     c = c.caller;
260                 }
261             }
262             return ev;
263         },
264
265
266         getCharCode: function(ev) {
267             ev = ev.browserEvent || ev;
268             return ev.charCode || ev.keyCode || 0;
269         },
270
271
272         _getCacheIndex: function(el, eventName, fn) {
273             for (var i = 0,len = listeners.length; i < len; ++i) {
274                 var li = listeners[i];
275                 if (li &&
276                     li[this.FN] == fn &&
277                     li[this.EL] == el &&
278                     li[this.TYPE] == eventName) {
279                     return i;
280                 }
281             }
282
283             return -1;
284         },
285
286
287         elCache: {},
288
289
290         getEl: function(id) {
291             return document.getElementById(id);
292         },
293
294
295         clearCache: function() {
296         },
297
298
299         _load: function(e) {
300             loadComplete = true;
301             var EU = Roo.lib.Event;
302
303
304             if (Roo.isIE) {
305                 EU.doRemove(window, "load", EU._load);
306             }
307         },
308
309
310         _tryPreloadAttach: function() {
311
312             if (this.locked) {
313                 return false;
314             }
315
316             this.locked = true;
317
318
319             var tryAgain = !loadComplete;
320             if (!tryAgain) {
321                 tryAgain = (retryCount > 0);
322             }
323
324
325             var notAvail = [];
326             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
327                 var item = onAvailStack[i];
328                 if (item) {
329                     var el = this.getEl(item.id);
330
331                     if (el) {
332                         if (!item.checkReady ||
333                             loadComplete ||
334                             el.nextSibling ||
335                             (document && document.body)) {
336
337                             var scope = el;
338                             if (item.override) {
339                                 if (item.override === true) {
340                                     scope = item.obj;
341                                 } else {
342                                     scope = item.override;
343                                 }
344                             }
345                             item.fn.call(scope, item.obj);
346                             onAvailStack[i] = null;
347                         }
348                     } else {
349                         notAvail.push(item);
350                     }
351                 }
352             }
353
354             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
355
356             if (tryAgain) {
357
358                 this.startInterval();
359             } else {
360                 clearInterval(this._interval);
361                 this._interval = null;
362             }
363
364             this.locked = false;
365
366             return true;
367
368         },
369
370
371         purgeElement: function(el, recurse, eventName) {
372             var elListeners = this.getListeners(el, eventName);
373             if (elListeners) {
374                 for (var i = 0,len = elListeners.length; i < len; ++i) {
375                     var l = elListeners[i];
376                     this.removeListener(el, l.type, l.fn);
377                 }
378             }
379
380             if (recurse && el && el.childNodes) {
381                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
382                     this.purgeElement(el.childNodes[i], recurse, eventName);
383                 }
384             }
385         },
386
387
388         getListeners: function(el, eventName) {
389             var results = [], searchLists;
390             if (!eventName) {
391                 searchLists = [listeners, unloadListeners];
392             } else if (eventName == "unload") {
393                 searchLists = [unloadListeners];
394             } else {
395                 searchLists = [listeners];
396             }
397
398             for (var j = 0; j < searchLists.length; ++j) {
399                 var searchList = searchLists[j];
400                 if (searchList && searchList.length > 0) {
401                     for (var i = 0,len = searchList.length; i < len; ++i) {
402                         var l = searchList[i];
403                         if (l && l[this.EL] === el &&
404                             (!eventName || eventName === l[this.TYPE])) {
405                             results.push({
406                                 type:   l[this.TYPE],
407                                 fn:     l[this.FN],
408                                 obj:    l[this.OBJ],
409                                 adjust: l[this.ADJ_SCOPE],
410                                 index:  i
411                             });
412                         }
413                     }
414                 }
415             }
416
417             return (results.length) ? results : null;
418         },
419
420
421         _unload: function(e) {
422
423             var EU = Roo.lib.Event, i, j, l, len, index;
424
425             for (i = 0,len = unloadListeners.length; i < len; ++i) {
426                 l = unloadListeners[i];
427                 if (l) {
428                     var scope = window;
429                     if (l[EU.ADJ_SCOPE]) {
430                         if (l[EU.ADJ_SCOPE] === true) {
431                             scope = l[EU.OBJ];
432                         } else {
433                             scope = l[EU.ADJ_SCOPE];
434                         }
435                     }
436                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
437                     unloadListeners[i] = null;
438                     l = null;
439                     scope = null;
440                 }
441             }
442
443             unloadListeners = null;
444
445             if (listeners && listeners.length > 0) {
446                 j = listeners.length;
447                 while (j) {
448                     index = j - 1;
449                     l = listeners[index];
450                     if (l) {
451                         EU.removeListener(l[EU.EL], l[EU.TYPE],
452                                 l[EU.FN], index);
453                     }
454                     j = j - 1;
455                 }
456                 l = null;
457
458                 EU.clearCache();
459             }
460
461             EU.doRemove(window, "unload", EU._unload);
462
463         },
464
465
466         getScroll: function() {
467             var dd = document.documentElement, db = document.body;
468             if (dd && (dd.scrollTop || dd.scrollLeft)) {
469                 return [dd.scrollTop, dd.scrollLeft];
470             } else if (db) {
471                 return [db.scrollTop, db.scrollLeft];
472             } else {
473                 return [0, 0];
474             }
475         },
476
477
478         doAdd: function () {
479             if (window.addEventListener) {
480                 return function(el, eventName, fn, capture) {
481                     el.addEventListener(eventName, fn, (capture));
482                 };
483             } else if (window.attachEvent) {
484                 return function(el, eventName, fn, capture) {
485                     el.attachEvent("on" + eventName, fn);
486                 };
487             } else {
488                 return function() {
489                 };
490             }
491         }(),
492
493
494         doRemove: function() {
495             if (window.removeEventListener) {
496                 return function (el, eventName, fn, capture) {
497                     el.removeEventListener(eventName, fn, (capture));
498                 };
499             } else if (window.detachEvent) {
500                 return function (el, eventName, fn) {
501                     el.detachEvent("on" + eventName, fn);
502                 };
503             } else {
504                 return function() {
505                 };
506             }
507         }()
508     };
509     
510 }();
511 (function() {     
512    
513     var E = Roo.lib.Event;
514     E.on = E.addListener;
515     E.un = E.removeListener;
516
517     if (document && document.body) {
518         E._load();
519     } else {
520         E.doAdd(window, "load", E._load);
521     }
522     E.doAdd(window, "unload", E._unload);
523     E._tryPreloadAttach();
524 })();
525