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