Upgrade to ExtJS 3.2.2 - Released 06/02/2010
[extjs.git] / src / ext-core / src / adapter / ext-base-event.js
1 /*!
2  * Ext JS Library 3.2.2
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.lib.Event = function() {
8     var loadComplete = false,
9         unloadListeners = {},
10         retryCount = 0,
11         onAvailStack = [],
12         _interval,
13         locked = false,
14         win = window,
15         doc = document,
16
17         // constants
18         POLL_RETRYS = 200,
19         POLL_INTERVAL = 20,
20         TYPE = 0,
21         FN = 1,
22         OBJ = 2,
23         ADJ_SCOPE = 3,
24         SCROLLLEFT = 'scrollLeft',
25         SCROLLTOP = 'scrollTop',
26         UNLOAD = 'unload',
27         MOUSEOVER = 'mouseover',
28         MOUSEOUT = 'mouseout',
29         // private
30         doAdd = function() {
31             var ret;
32             if (win.addEventListener) {
33                 ret = function(el, eventName, fn, capture) {
34                     if (eventName == 'mouseenter') {
35                         fn = fn.createInterceptor(checkRelatedTarget);
36                         el.addEventListener(MOUSEOVER, fn, (capture));
37                     } else if (eventName == 'mouseleave') {
38                         fn = fn.createInterceptor(checkRelatedTarget);
39                         el.addEventListener(MOUSEOUT, fn, (capture));
40                     } else {
41                         el.addEventListener(eventName, fn, (capture));
42                     }
43                     return fn;
44                 };
45             } else if (win.attachEvent) {
46                 ret = function(el, eventName, fn, capture) {
47                     el.attachEvent("on" + eventName, fn);
48                     return fn;
49                 };
50             } else {
51                 ret = function(){};
52             }
53             return ret;
54         }(),
55         // private
56         doRemove = function(){
57             var ret;
58             if (win.removeEventListener) {
59                 ret = function (el, eventName, fn, capture) {
60                     if (eventName == 'mouseenter') {
61                         eventName = MOUSEOVER;
62                     } else if (eventName == 'mouseleave') {
63                         eventName = MOUSEOUT;
64                     }
65                     el.removeEventListener(eventName, fn, (capture));
66                 };
67             } else if (win.detachEvent) {
68                 ret = function (el, eventName, fn) {
69                     el.detachEvent("on" + eventName, fn);
70                 };
71             } else {
72                 ret = function(){};
73             }
74             return ret;
75         }();
76
77     function checkRelatedTarget(e) {
78         return !elContains(e.currentTarget, pub.getRelatedTarget(e));
79     }
80
81     function elContains(parent, child) {
82        if(parent && parent.firstChild){
83          while(child) {
84             if(child === parent) {
85                 return true;
86             }
87             child = child.parentNode;
88             if(child && (child.nodeType != 1)) {
89                 child = null;
90             }
91           }
92         }
93         return false;
94     }
95
96     // private
97     function _tryPreloadAttach() {
98         var ret = false,
99             notAvail = [],
100             element, i, v, override,
101             tryAgain = !loadComplete || (retryCount > 0);
102
103         if(!locked){
104             locked = true;
105             
106             for(i = 0; i < onAvailStack.length; ++i){
107                 v = onAvailStack[i];
108                 if(v && (element = doc.getElementById(v.id))){
109                     if(!v.checkReady || loadComplete || element.nextSibling || (doc && doc.body)) {
110                         override = v.override;
111                         element = override ? (override === true ? v.obj : override) : element;
112                         v.fn.call(element, v.obj);
113                         onAvailStack.remove(v);
114                         --i;
115                     }else{
116                         notAvail.push(v);
117                     }
118                 }
119             }
120
121             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
122
123             if (tryAgain) {
124                 startInterval();
125             } else {
126                 clearInterval(_interval);
127                 _interval = null;
128             }
129             ret = !(locked = false);
130         }
131         return ret;
132     }
133
134     // private
135     function startInterval() {
136         if(!_interval){
137             var callback = function() {
138                 _tryPreloadAttach();
139             };
140             _interval = setInterval(callback, POLL_INTERVAL);
141         }
142     }
143
144     // private
145     function getScroll() {
146         var dd = doc.documentElement,
147             db = doc.body;
148         if(dd && (dd[SCROLLTOP] || dd[SCROLLLEFT])){
149             return [dd[SCROLLLEFT], dd[SCROLLTOP]];
150         }else if(db){
151             return [db[SCROLLLEFT], db[SCROLLTOP]];
152         }else{
153             return [0, 0];
154         }
155     }
156
157     // private
158     function getPageCoord (ev, xy) {
159         ev = ev.browserEvent || ev;
160         var coord  = ev['page' + xy];
161         if (!coord && coord !== 0) {
162             coord = ev['client' + xy] || 0;
163
164             if (Ext.isIE) {
165                 coord += getScroll()[xy == "X" ? 0 : 1];
166             }
167         }
168
169         return coord;
170     }
171
172     var pub =  {
173         extAdapter: true,
174         onAvailable : function(p_id, p_fn, p_obj, p_override) {
175             onAvailStack.push({
176                 id:         p_id,
177                 fn:         p_fn,
178                 obj:        p_obj,
179                 override:   p_override,
180                 checkReady: false });
181
182             retryCount = POLL_RETRYS;
183             startInterval();
184         },
185
186         // This function should ALWAYS be called from Ext.EventManager
187         addListener: function(el, eventName, fn) {
188             el = Ext.getDom(el);
189             if (el && fn) {
190                 if (eventName == UNLOAD) {
191                     if (unloadListeners[el.id] === undefined) {
192                         unloadListeners[el.id] = [];
193                     }
194                     unloadListeners[el.id].push([eventName, fn]);
195                     return fn;
196                 }
197                 return doAdd(el, eventName, fn, false);
198             }
199             return false;
200         },
201
202         // This function should ALWAYS be called from Ext.EventManager
203         removeListener: function(el, eventName, fn) {
204             el = Ext.getDom(el);
205             var i, len, li, lis;
206             if (el && fn) {
207                 if(eventName == UNLOAD){
208                     if((lis = unloadListeners[el.id]) !== undefined){
209                         for(i = 0, len = lis.length; i < len; i++){
210                             if((li = lis[i]) && li[TYPE] == eventName && li[FN] == fn){
211                                 unloadListeners[el.id].splice(i, 1);
212                             }
213                         }
214                     }
215                     return;
216                 }
217                 doRemove(el, eventName, fn, false);
218             }
219         },
220
221         getTarget : function(ev) {
222             ev = ev.browserEvent || ev;
223             return this.resolveTextNode(ev.target || ev.srcElement);
224         },
225
226         resolveTextNode : Ext.isGecko ? function(node){
227             if(!node){
228                 return;
229             }
230             // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
231             var s = HTMLElement.prototype.toString.call(node);
232             if(s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]'){
233                 return;
234             }
235             return node.nodeType == 3 ? node.parentNode : node;
236         } : function(node){
237             return node && node.nodeType == 3 ? node.parentNode : node;
238         },
239
240         getRelatedTarget : function(ev) {
241             ev = ev.browserEvent || ev;
242             return this.resolveTextNode(ev.relatedTarget ||
243                 (/(mouseout|mouseleave)/.test(ev.type) ? ev.toElement :
244                  /(mouseover|mouseenter)/.test(ev.type) ? ev.fromElement : null));
245         },
246
247         getPageX : function(ev) {
248             return getPageCoord(ev, "X");
249         },
250
251         getPageY : function(ev) {
252             return getPageCoord(ev, "Y");
253         },
254
255
256         getXY : function(ev) {
257             return [this.getPageX(ev), this.getPageY(ev)];
258         },
259
260         stopEvent : function(ev) {
261             this.stopPropagation(ev);
262             this.preventDefault(ev);
263         },
264
265         stopPropagation : function(ev) {
266             ev = ev.browserEvent || ev;
267             if (ev.stopPropagation) {
268                 ev.stopPropagation();
269             } else {
270                 ev.cancelBubble = true;
271             }
272         },
273
274         preventDefault : function(ev) {
275             ev = ev.browserEvent || ev;
276             if (ev.preventDefault) {
277                 ev.preventDefault();
278             } else {
279                 ev.returnValue = false;
280             }
281         },
282
283         getEvent : function(e) {
284             e = e || win.event;
285             if (!e) {
286                 var c = this.getEvent.caller;
287                 while (c) {
288                     e = c.arguments[0];
289                     if (e && Event == e.constructor) {
290                         break;
291                     }
292                     c = c.caller;
293                 }
294             }
295             return e;
296         },
297
298         getCharCode : function(ev) {
299             ev = ev.browserEvent || ev;
300             return ev.charCode || ev.keyCode || 0;
301         },
302
303         //clearCache: function() {},
304         // deprecated, call from EventManager
305         getListeners : function(el, eventName) {
306             Ext.EventManager.getListeners(el, eventName);
307         },
308
309         // deprecated, call from EventManager
310         purgeElement : function(el, recurse, eventName) {
311             Ext.EventManager.purgeElement(el, recurse, eventName);
312         },
313
314         _load : function(e) {
315             loadComplete = true;
316             
317             if (Ext.isIE && e !== true) {
318                 // IE8 complains that _load is null or not an object
319                 // so lets remove self via arguments.callee
320                 doRemove(win, "load", arguments.callee);
321             }
322         },
323
324         _unload : function(e) {
325              var EU = Ext.lib.Event,
326                 i, v, ul, id, len, scope;
327
328             for (id in unloadListeners) {
329                 ul = unloadListeners[id];
330                 for (i = 0, len = ul.length; i < len; i++) {
331                     v = ul[i];
332                     if (v) {
333                         try{
334                             scope = v[ADJ_SCOPE] ? (v[ADJ_SCOPE] === true ? v[OBJ] : v[ADJ_SCOPE]) :  win;
335                             v[FN].call(scope, EU.getEvent(e), v[OBJ]);
336                         }catch(ex){}
337                     }
338                 }
339             };
340
341             Ext.EventManager._unload();
342
343             doRemove(win, UNLOAD, EU._unload);
344         }
345     };
346
347     // Initialize stuff.
348     pub.on = pub.addListener;
349     pub.un = pub.removeListener;
350     if (doc && doc.body) {
351         pub._load(true);
352     } else {
353         doAdd(win, "load", pub._load);
354     }
355     doAdd(win, UNLOAD, pub._unload);
356     _tryPreloadAttach();
357
358     return pub;
359 }();