Roo/lib/Event.js
[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             var t = ev.target || ev.srcElement;
137             return this.resolveTextNode(t);
138         },
139
140
141         resolveTextNode: function(node) {
142             if (Roo.isSafari && node && 3 == node.nodeType) {
143                 return node.parentNode;
144             } else {
145                 return node;
146             }
147         },
148
149
150         getPageX: function(ev) {
151             ev = ev.browserEvent || ev;
152             ev = Roo.isTouch ? ev.touches[0] : ev;
153             var x = ev.pageX;
154             if (!x && 0 !== x) {
155                 x = ev.clientX || 0;
156
157                 if (Roo.isIE) {
158                     x += this.getScroll()[1];
159                 }
160             }
161
162             return x;
163         },
164
165
166         getPageY: function(ev) {
167             ev = ev.browserEvent || ev;
168             ev = Roo.isTouch ? ev.touches[0] : ev;
169             var y = ev.pageY;
170             if (!y && 0 !== y) {
171                 y = ev.clientY || 0;
172
173                 if (Roo.isIE) {
174                     y += this.getScroll()[0];
175                 }
176             }
177
178
179             return y;
180         },
181
182
183         getXY: function(ev) {
184             ev = ev.browserEvent || ev;
185             ev = Roo.isTouch ? ev.touches[0] : ev;
186             return [this.getPageX(ev), this.getPageY(ev)];
187         },
188
189
190         getRelatedTarget: function(ev) {
191             ev = ev.browserEvent || ev;
192             ev = Roo.isTouch ? ev.touches[0] : ev;
193             var t = ev.relatedTarget;
194             if (!t) {
195                 if (ev.type == "mouseout") {
196                     t = ev.toElement;
197                 } else if (ev.type == "mouseover") {
198                     t = ev.fromElement;
199                 }
200             }
201
202             return this.resolveTextNode(t);
203         },
204
205
206         getTime: function(ev) {
207             ev = ev.browserEvent || ev;
208             ev = Roo.isTouch ? ev.touches[0] : ev;
209             if (!ev.time) {
210                 var t = new Date().getTime();
211                 try {
212                     ev.time = t;
213                 } catch(ex) {
214                     this.lastError = ex;
215                     return t;
216                 }
217             }
218
219             return ev.time;
220         },
221
222
223         stopEvent: function(ev) {
224             this.stopPropagation(ev);
225             this.preventDefault(ev);
226         },
227
228
229         stopPropagation: function(ev) {
230             ev = ev.browserEvent || ev;
231             if (ev.stopPropagation) {
232                 ev.stopPropagation();
233             } else {
234                 ev.cancelBubble = true;
235             }
236         },
237
238
239         preventDefault: function(ev) {
240             ev = ev.browserEvent || ev;
241             if(ev.preventDefault) {
242                 ev.preventDefault();
243             } else {
244                 ev.returnValue = false;
245             }
246         },
247
248
249         getEvent: function(e) {
250             var ev = e || window.event;
251             if (!ev) {
252                 var c = this.getEvent.caller;
253                 while (c) {
254                     ev = c.arguments[0];
255                     if (ev && Event == ev.constructor) {
256                         break;
257                     }
258                     c = c.caller;
259                 }
260             }
261             return ev;
262         },
263
264
265         getCharCode: function(ev) {
266             ev = ev.browserEvent || ev;
267             return ev.charCode || ev.keyCode || 0;
268         },
269
270
271         _getCacheIndex: function(el, eventName, fn) {
272             for (var i = 0,len = listeners.length; i < len; ++i) {
273                 var li = listeners[i];
274                 if (li &&
275                     li[this.FN] == fn &&
276                     li[this.EL] == el &&
277                     li[this.TYPE] == eventName) {
278                     return i;
279                 }
280             }
281
282             return -1;
283         },
284
285
286         elCache: {},
287
288
289         getEl: function(id) {
290             return document.getElementById(id);
291         },
292
293
294         clearCache: function() {
295         },
296
297
298         _load: function(e) {
299             loadComplete = true;
300             var EU = Roo.lib.Event;
301
302
303             if (Roo.isIE) {
304                 EU.doRemove(window, "load", EU._load);
305             }
306         },
307
308
309         _tryPreloadAttach: function() {
310
311             if (this.locked) {
312                 return false;
313             }
314
315             this.locked = true;
316
317
318             var tryAgain = !loadComplete;
319             if (!tryAgain) {
320                 tryAgain = (retryCount > 0);
321             }
322
323
324             var notAvail = [];
325             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
326                 var item = onAvailStack[i];
327                 if (item) {
328                     var el = this.getEl(item.id);
329
330                     if (el) {
331                         if (!item.checkReady ||
332                             loadComplete ||
333                             el.nextSibling ||
334                             (document && document.body)) {
335
336                             var scope = el;
337                             if (item.override) {
338                                 if (item.override === true) {
339                                     scope = item.obj;
340                                 } else {
341                                     scope = item.override;
342                                 }
343                             }
344                             item.fn.call(scope, item.obj);
345                             onAvailStack[i] = null;
346                         }
347                     } else {
348                         notAvail.push(item);
349                     }
350                 }
351             }
352
353             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
354
355             if (tryAgain) {
356
357                 this.startInterval();
358             } else {
359                 clearInterval(this._interval);
360                 this._interval = null;
361             }
362
363             this.locked = false;
364
365             return true;
366
367         },
368
369
370         purgeElement: function(el, recurse, eventName) {
371             var elListeners = this.getListeners(el, eventName);
372             if (elListeners) {
373                 for (var i = 0,len = elListeners.length; i < len; ++i) {
374                     var l = elListeners[i];
375                     this.removeListener(el, l.type, l.fn);
376                 }
377             }
378
379             if (recurse && el && el.childNodes) {
380                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
381                     this.purgeElement(el.childNodes[i], recurse, eventName);
382                 }
383             }
384         },
385
386
387         getListeners: function(el, eventName) {
388             var results = [], searchLists;
389             if (!eventName) {
390                 searchLists = [listeners, unloadListeners];
391             } else if (eventName == "unload") {
392                 searchLists = [unloadListeners];
393             } else {
394                 searchLists = [listeners];
395             }
396
397             for (var j = 0; j < searchLists.length; ++j) {
398                 var searchList = searchLists[j];
399                 if (searchList && searchList.length > 0) {
400                     for (var i = 0,len = searchList.length; i < len; ++i) {
401                         var l = searchList[i];
402                         if (l && l[this.EL] === el &&
403                             (!eventName || eventName === l[this.TYPE])) {
404                             results.push({
405                                 type:   l[this.TYPE],
406                                 fn:     l[this.FN],
407                                 obj:    l[this.OBJ],
408                                 adjust: l[this.ADJ_SCOPE],
409                                 index:  i
410                             });
411                         }
412                     }
413                 }
414             }
415
416             return (results.length) ? results : null;
417         },
418
419
420         _unload: function(e) {
421
422             var EU = Roo.lib.Event, i, j, l, len, index;
423
424             for (i = 0,len = unloadListeners.length; i < len; ++i) {
425                 l = unloadListeners[i];
426                 if (l) {
427                     var scope = window;
428                     if (l[EU.ADJ_SCOPE]) {
429                         if (l[EU.ADJ_SCOPE] === true) {
430                             scope = l[EU.OBJ];
431                         } else {
432                             scope = l[EU.ADJ_SCOPE];
433                         }
434                     }
435                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
436                     unloadListeners[i] = null;
437                     l = null;
438                     scope = null;
439                 }
440             }
441
442             unloadListeners = null;
443
444             if (listeners && listeners.length > 0) {
445                 j = listeners.length;
446                 while (j) {
447                     index = j - 1;
448                     l = listeners[index];
449                     if (l) {
450                         EU.removeListener(l[EU.EL], l[EU.TYPE],
451                                 l[EU.FN], index);
452                     }
453                     j = j - 1;
454                 }
455                 l = null;
456
457                 EU.clearCache();
458             }
459
460             EU.doRemove(window, "unload", EU._unload);
461
462         },
463
464
465         getScroll: function() {
466             var dd = document.documentElement, db = document.body;
467             if (dd && (dd.scrollTop || dd.scrollLeft)) {
468                 return [dd.scrollTop, dd.scrollLeft];
469             } else if (db) {
470                 return [db.scrollTop, db.scrollLeft];
471             } else {
472                 return [0, 0];
473             }
474         },
475
476
477         doAdd: function () {
478             if (window.addEventListener) {
479                 return function(el, eventName, fn, capture) {
480                     el.addEventListener(eventName, fn, (capture));
481                 };
482             } else if (window.attachEvent) {
483                 return function(el, eventName, fn, capture) {
484                     el.attachEvent("on" + eventName, fn);
485                 };
486             } else {
487                 return function() {
488                 };
489             }
490         }(),
491
492
493         doRemove: function() {
494             if (window.removeEventListener) {
495                 return function (el, eventName, fn, capture) {
496                     el.removeEventListener(eventName, fn, (capture));
497                 };
498             } else if (window.detachEvent) {
499                 return function (el, eventName, fn) {
500                     el.detachEvent("on" + eventName, fn);
501                 };
502             } else {
503                 return function() {
504                 };
505             }
506         }()
507     };
508     
509 }();
510 (function() {     
511    
512     var E = Roo.lib.Event;
513     E.on = E.addListener;
514     E.un = E.removeListener;
515
516     if (document && document.body) {
517         E._load();
518     } else {
519         E.doAdd(window, "load", E._load);
520     }
521     E.doAdd(window, "unload", E._unload);
522     E._tryPreloadAttach();
523 })();
524