commit extjs-2.2.1
[extjs.git] / source / core / EventManager.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.EventManager\r
11  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides\r
12  * several useful events directly.\r
13  * See {@link Ext.EventObject} for more details on normalized event objects.\r
14  * @singleton\r
15  */\r
16 Ext.EventManager = function(){\r
17     var docReadyEvent, docReadyProcId, docReadyState = false;\r
18     var resizeEvent, resizeTask, textEvent, textSize;\r
19     var E = Ext.lib.Event;\r
20     var D = Ext.lib.Dom;\r
21     // fix parser confusion\r
22     var xname = 'Ex' + 't';\r
23 \r
24     var elHash = {};\r
25 \r
26     var addListener = function(el, ename, fn, wrap, scope){\r
27         var id = Ext.id(el);\r
28         if(!elHash[id]){\r
29             elHash[id] = {};\r
30         }\r
31         var es = elHash[id];\r
32         if(!es[ename]){\r
33             es[ename] = [];\r
34         }\r
35         var ls = es[ename];\r
36         ls.push({\r
37             id: id,\r
38             ename: ename,\r
39             fn: fn,\r
40             wrap: wrap,\r
41             scope: scope\r
42         });\r
43 \r
44          E.on(el, ename, wrap);\r
45 \r
46         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery\r
47             el.addEventListener("DOMMouseScroll", wrap, false);\r
48             E.on(window, 'unload', function(){\r
49                 el.removeEventListener("DOMMouseScroll", wrap, false);\r
50             });\r
51         }\r
52         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document\r
53             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);\r
54         }\r
55     }\r
56 \r
57     var removeListener = function(el, ename, fn, scope){\r
58         el = Ext.getDom(el);\r
59 \r
60         var id = Ext.id(el), es = elHash[id], wrap;\r
61         if(es){\r
62             var ls = es[ename], l;\r
63             if(ls){\r
64                 for(var i = 0, len = ls.length; i < len; i++){\r
65                     l = ls[i];\r
66                     if(l.fn == fn && (!scope || l.scope == scope)){\r
67                         wrap = l.wrap;\r
68                         E.un(el, ename, wrap);\r
69                         ls.splice(i, 1);\r
70                         break;\r
71                     }\r
72                 }\r
73             }\r
74         }\r
75         if(ename == "mousewheel" && el.addEventListener && wrap){\r
76             el.removeEventListener("DOMMouseScroll", wrap, false);\r
77         }\r
78         if(ename == "mousedown" && el == document && wrap){ // fix stopped mousedowns on the document\r
79             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);\r
80         }\r
81     }\r
82 \r
83     var removeAll = function(el){\r
84         el = Ext.getDom(el);\r
85         var id = Ext.id(el), es = elHash[id], ls;\r
86         if(es){\r
87             for(var ename in es){\r
88                 if(es.hasOwnProperty(ename)){\r
89                     ls = es[ename];\r
90                     for(var i = 0, len = ls.length; i < len; i++){\r
91                         E.un(el, ename, ls[i].wrap);\r
92                         ls[i] = null;\r
93                     }\r
94                 }\r
95                 es[ename] = null;\r
96             }\r
97             delete elHash[id];\r
98         }\r
99     }\r
100 \r
101 \r
102     var fireDocReady = function(){\r
103         if(!docReadyState){\r
104             docReadyState = true;\r
105             Ext.isReady = true;\r
106             if(docReadyProcId){\r
107                 clearInterval(docReadyProcId);\r
108             }\r
109             if(Ext.isGecko || Ext.isOpera) {\r
110                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);\r
111             }\r
112             if(Ext.isIE){\r
113                 var defer = document.getElementById("ie-deferred-loader");\r
114                 if(defer){\r
115                     defer.onreadystatechange = null;\r
116                     defer.parentNode.removeChild(defer);\r
117                 }\r
118             }\r
119             if(docReadyEvent){\r
120                 docReadyEvent.fire();\r
121                 docReadyEvent.clearListeners();\r
122             }\r
123         }\r
124     };\r
125 \r
126     var initDocReady = function(){\r
127         docReadyEvent = new Ext.util.Event();\r
128         if(Ext.isGecko || Ext.isOpera) {\r
129             document.addEventListener("DOMContentLoaded", fireDocReady, false);\r
130         }else if(Ext.isIE){\r
131             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");\r
132             var defer = document.getElementById("ie-deferred-loader");\r
133             defer.onreadystatechange = function(){\r
134                 if(this.readyState == "complete"){\r
135                     fireDocReady();\r
136                 }\r
137             };\r
138         }else if(Ext.isSafari){\r
139             docReadyProcId = setInterval(function(){\r
140                 var rs = document.readyState;\r
141                 if(rs == "complete") {\r
142                     fireDocReady();\r
143                  }\r
144             }, 10);\r
145         }\r
146         // no matter what, make sure it fires on load\r
147         E.on(window, "load", fireDocReady);\r
148     };\r
149 \r
150     var createBuffered = function(h, o){\r
151         var task = new Ext.util.DelayedTask(h);\r
152         return function(e){\r
153             // create new event object impl so new events don't wipe out properties\r
154             e = new Ext.EventObjectImpl(e);\r
155             task.delay(o.buffer, h, null, [e]);\r
156         };\r
157     };\r
158 \r
159     var createSingle = function(h, el, ename, fn, scope){\r
160         return function(e){\r
161             Ext.EventManager.removeListener(el, ename, fn, scope);\r
162             h(e);\r
163         };\r
164     };\r
165 \r
166     var createDelayed = function(h, o){\r
167         return function(e){\r
168             // create new event object impl so new events don't wipe out properties\r
169             e = new Ext.EventObjectImpl(e);\r
170             setTimeout(function(){\r
171                 h(e);\r
172             }, o.delay || 10);\r
173         };\r
174     };\r
175 \r
176     var listen = function(element, ename, opt, fn, scope){\r
177         var o = (!opt || typeof opt == "boolean") ? {} : opt;\r
178         fn = fn || o.fn; scope = scope || o.scope;\r
179         var el = Ext.getDom(element);\r
180         if(!el){\r
181             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';\r
182         }\r
183         var h = function(e){\r
184             // prevent errors while unload occurring\r
185             if(!window[xname]){\r
186                 return;\r
187             }\r
188             e = Ext.EventObject.setEvent(e);\r
189             var t;\r
190             if(o.delegate){\r
191                 t = e.getTarget(o.delegate, el);\r
192                 if(!t){\r
193                     return;\r
194                 }\r
195             }else{\r
196                 t = e.target;\r
197             }\r
198             if(o.stopEvent === true){\r
199                 e.stopEvent();\r
200             }\r
201             if(o.preventDefault === true){\r
202                e.preventDefault();\r
203             }\r
204             if(o.stopPropagation === true){\r
205                 e.stopPropagation();\r
206             }\r
207 \r
208             if(o.normalized === false){\r
209                 e = e.browserEvent;\r
210             }\r
211 \r
212             fn.call(scope || el, e, t, o);\r
213         };\r
214         if(o.delay){\r
215             h = createDelayed(h, o);\r
216         }\r
217         if(o.single){\r
218             h = createSingle(h, el, ename, fn, scope);\r
219         }\r
220         if(o.buffer){\r
221             h = createBuffered(h, o);\r
222         }\r
223 \r
224         addListener(el, ename, fn, h, scope);\r
225         return h;\r
226     };\r
227 \r
228     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;\r
229     var pub = {\r
230 \r
231     /**\r
232      * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will\r
233      * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.\r
234      * @param {String/HTMLElement} el The html element or id to assign the event handler to\r
235      * @param {String} eventName The type of event to listen for\r
236      * @param {Function} handler The handler function the event invokes This function is passed\r
237      * the following parameters:<ul>\r
238      * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>\r
239      * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.\r
240      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>\r
241      * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>\r
242      * </ul>\r
243      * @param {Object} scope (optional) The scope in which to execute the handler\r
244      * function (the handler function's "this" context)\r
245      * @param {Object} options (optional) An object containing handler configuration properties.\r
246      * This may contain any of the following properties:<ul>\r
247      * <li>scope {Object} : The scope in which to execute the handler function. The handler function's "this" context.</li>\r
248      * <li>delegate {String} : A simple selector to filter the target or look for a descendant of the target</li>\r
249      * <li>stopEvent {Boolean} : True to stop the event. That is stop propagation, and prevent the default action.</li>\r
250      * <li>preventDefault {Boolean} : True to prevent the default action</li>\r
251      * <li>stopPropagation {Boolean} : True to prevent event propagation</li>\r
252      * <li>normalized {Boolean} : False to pass a browser event to the handler function instead of an Ext.EventObject</li>\r
253      * <li>delay {Number} : The number of milliseconds to delay the invocation of the handler after te event fires.</li>\r
254      * <li>single {Boolean} : True to add a handler to handle just the next firing of the event, and then remove itself.</li>\r
255      * <li>buffer {Number} : Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed\r
256      * by the specified number of milliseconds. If the event fires again within that time, the original\r
257      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>\r
258      * </ul><br>\r
259      * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>\r
260      */\r
261         addListener : function(element, eventName, fn, scope, options){\r
262             if(typeof eventName == "object"){\r
263                 var o = eventName;\r
264                 for(var e in o){\r
265                     if(propRe.test(e)){\r
266                         continue;\r
267                     }\r
268                     if(typeof o[e] == "function"){\r
269                         // shared options\r
270                         listen(element, e, o, o[e], o.scope);\r
271                     }else{\r
272                         // individual options\r
273                         listen(element, e, o[e]);\r
274                     }\r
275                 }\r
276                 return;\r
277             }\r
278             return listen(element, eventName, options, fn, scope);\r
279         },\r
280 \r
281         /**\r
282          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically\r
283          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.\r
284          * @param {String/HTMLElement} el The id or html element from which to remove the event\r
285          * @param {String} eventName The type of event\r
286          * @param {Function} fn The handler function to remove\r
287          */\r
288         removeListener : function(element, eventName, fn, scope){\r
289             return removeListener(element, eventName, fn, scope);\r
290         },\r
291 \r
292         /**\r
293          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}\r
294          * directly on an Element in favor of calling this version.\r
295          * @param {String/HTMLElement} el The id or html element from which to remove the event\r
296          */\r
297         removeAll : function(element){\r
298             return removeAll(element);\r
299         },\r
300 \r
301         /**\r
302          * Fires when the document is ready (before onload and before images are loaded). Can be\r
303          * accessed shorthanded as Ext.onReady().\r
304          * @param {Function} fn The method the event invokes\r
305          * @param {Object} scope (optional) An object that becomes the scope of the handler\r
306          * @param {boolean} options (optional) An object containing standard {@link #addListener} options\r
307          */\r
308         onDocumentReady : function(fn, scope, options){\r
309             if(docReadyState){ // if it already fired\r
310                 docReadyEvent.addListener(fn, scope, options);\r
311                 docReadyEvent.fire();\r
312                 docReadyEvent.clearListeners();\r
313                 return;\r
314             }\r
315             if(!docReadyEvent){\r
316                 initDocReady();\r
317             }\r
318             options = options || {};\r
319             if(!options.delay){\r
320                 options.delay = 1;\r
321             }\r
322             docReadyEvent.addListener(fn, scope, options);\r
323         },\r
324         \r
325         // private\r
326         doResizeEvent: function(){\r
327             resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
328         },\r
329 \r
330         /**\r
331          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.\r
332          * @param {Function} fn        The method the event invokes\r
333          * @param {Object}   scope    An object that becomes the scope of the handler\r
334          * @param {boolean}  options\r
335          */\r
336         onWindowResize : function(fn, scope, options){\r
337             if(!resizeEvent){\r
338                 resizeEvent = new Ext.util.Event();\r
339                 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);\r
340                 E.on(window, "resize", this.fireWindowResize, this);\r
341             }\r
342             resizeEvent.addListener(fn, scope, options);\r
343         },\r
344 \r
345         // exposed only to allow manual firing\r
346         fireWindowResize : function(){\r
347             if(resizeEvent){\r
348                 if((Ext.isIE||Ext.isAir) && resizeTask){\r
349                     resizeTask.delay(50);\r
350                 }else{\r
351                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
352                 }\r
353             }\r
354         },\r
355 \r
356         /**\r
357          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.\r
358          * @param {Function} fn        The method the event invokes\r
359          * @param {Object}   scope    An object that becomes the scope of the handler\r
360          * @param {boolean}  options\r
361          */\r
362         onTextResize : function(fn, scope, options){\r
363             if(!textEvent){\r
364                 textEvent = new Ext.util.Event();\r
365                 var textEl = new Ext.Element(document.createElement('div'));\r
366                 textEl.dom.className = 'x-text-resize';\r
367                 textEl.dom.innerHTML = 'X';\r
368                 textEl.appendTo(document.body);\r
369                 textSize = textEl.dom.offsetHeight;\r
370                 setInterval(function(){\r
371                     if(textEl.dom.offsetHeight != textSize){\r
372                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);\r
373                     }\r
374                 }, this.textResizeInterval);\r
375             }\r
376             textEvent.addListener(fn, scope, options);\r
377         },\r
378 \r
379         /**\r
380          * Removes the passed window resize listener.\r
381          * @param {Function} fn        The method the event invokes\r
382          * @param {Object}   scope    The scope of handler\r
383          */\r
384         removeResizeListener : function(fn, scope){\r
385             if(resizeEvent){\r
386                 resizeEvent.removeListener(fn, scope);\r
387             }\r
388         },\r
389 \r
390         // private\r
391         fireResize : function(){\r
392             if(resizeEvent){\r
393                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());\r
394             }\r
395         },\r
396         /**\r
397          * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)\r
398          */\r
399         ieDeferSrc : false,\r
400         /**\r
401          * The frequency, in milliseconds, to check for text resize events (defaults to 50)\r
402          */\r
403         textResizeInterval : 50\r
404     };\r
405      /**\r
406      * Appends an event handler to an element.  Shorthand for {@link #addListener}.\r
407      * @param {String/HTMLElement} el The html element or id to assign the event handler to\r
408      * @param {String} eventName The type of event to listen for\r
409      * @param {Function} handler The handler function the event invokes\r
410      * @param {Object} scope (optional) The scope in which to execute the handler\r
411      * function (the handler function's "this" context)\r
412      * @param {Object} options (optional) An object containing standard {@link #addListener} options\r
413      * @member Ext.EventManager\r
414      * @method on\r
415      */\r
416     pub.on = pub.addListener;\r
417     /**\r
418      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.\r
419      * @param {String/HTMLElement} el The id or html element from which to remove the event\r
420      * @param {String} eventName The type of event\r
421      * @param {Function} fn The handler function to remove\r
422      * @return {Boolean} True if a listener was actually removed, else false\r
423      * @member Ext.EventManager\r
424      * @method un\r
425      */\r
426     pub.un = pub.removeListener;\r
427 \r
428     pub.stoppedMouseDownEvent = new Ext.util.Event();\r
429     return pub;\r
430 }();\r
431 /**\r
432   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Ext.EventManager#onDocumentReady}.\r
433   * @param {Function} fn The method the event invokes\r
434   * @param {Object} scope An object that becomes the scope of the handler\r
435   * @param {boolean} options (optional) An object containing standard {@link #addListener} options\r
436   * @member Ext\r
437   * @method onReady\r
438  */\r
439 Ext.onReady = Ext.EventManager.onDocumentReady;\r
440 \r
441 \r
442 // Initialize doc classes\r
443 (function(){\r
444     var initExtCss = function(){\r
445         // find the body element\r
446         var bd = document.body || document.getElementsByTagName('body')[0];\r
447         if(!bd){ return false; }\r
448         var cls = [' ',\r
449                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))\r
450                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')\r
451                 : Ext.isOpera ? "ext-opera"\r
452                 : Ext.isSafari ? "ext-safari"\r
453                 : Ext.isChrome ? "ext-chrome" : ""];\r
454 \r
455         if(Ext.isMac){\r
456             cls.push("ext-mac");\r
457         }\r
458         if(Ext.isLinux){\r
459             cls.push("ext-linux");\r
460         }\r
461         if(Ext.isBorderBox){\r
462             cls.push('ext-border-box');\r
463         }\r
464         if(Ext.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"\r
465             var p = bd.parentNode;\r
466             if(p){\r
467                 p.className += ' ext-strict';\r
468             }\r
469         }\r
470         bd.className += cls.join(' ');\r
471         return true;\r
472     }\r
473 \r
474     if(!initExtCss()){\r
475         Ext.onReady(initExtCss);\r
476     }\r
477 })();\r
478 \r
479 /**\r
480  * @class Ext.EventObject\r
481  * EventObject exposes the Yahoo! UI Event functionality directly on the object\r
482  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code\r
483  * Example:\r
484  * <pre><code>\r
485  function handleClick(e){ // e is not a standard event object, it is a Ext.EventObject\r
486     e.preventDefault();\r
487     var target = e.getTarget();\r
488     ...\r
489  }\r
490  var myDiv = Ext.get("myDiv");\r
491  myDiv.on("click", handleClick);\r
492  //or\r
493  Ext.EventManager.on("myDiv", 'click', handleClick);\r
494  Ext.EventManager.addListener("myDiv", 'click', handleClick);\r
495  </code></pre>\r
496  * @singleton\r
497  */\r
498 Ext.EventObject = function(){\r
499 \r
500     var E = Ext.lib.Event;\r
501 \r
502     // safari keypress events for special keys return bad keycodes\r
503     var safariKeys = {\r
504         3 : 13, // enter\r
505         63234 : 37, // left\r
506         63235 : 39, // right\r
507         63232 : 38, // up\r
508         63233 : 40, // down\r
509         63276 : 33, // page up\r
510         63277 : 34, // page down\r
511         63272 : 46, // delete\r
512         63273 : 36, // home\r
513         63275 : 35  // end\r
514     };\r
515 \r
516     // normalize button clicks\r
517     var btnMap = Ext.isIE ? {1:0,4:1,2:2} :\r
518                 (Ext.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});\r
519 \r
520     Ext.EventObjectImpl = function(e){\r
521         if(e){\r
522             this.setEvent(e.browserEvent || e);\r
523         }\r
524     };\r
525 \r
526     Ext.EventObjectImpl.prototype = {\r
527         /** The normal browser event */\r
528         browserEvent : null,\r
529         /** The button pressed in a mouse event */\r
530         button : -1,\r
531         /** True if the shift key was down during the event */\r
532         shiftKey : false,\r
533         /** True if the control key was down during the event */\r
534         ctrlKey : false,\r
535         /** True if the alt key was down during the event */\r
536         altKey : false,\r
537 \r
538         /** Key constant @type Number */\r
539         BACKSPACE: 8,\r
540         /** Key constant @type Number */\r
541         TAB: 9,\r
542         /** Key constant @type Number */\r
543         NUM_CENTER: 12,\r
544         /** Key constant @type Number */\r
545         ENTER: 13,\r
546         /** Key constant @type Number */\r
547         RETURN: 13,\r
548         /** Key constant @type Number */\r
549         SHIFT: 16,\r
550         /** Key constant @type Number */\r
551         CTRL: 17,\r
552         CONTROL : 17, // legacy\r
553         /** Key constant @type Number */\r
554         ALT: 18,\r
555         /** Key constant @type Number */\r
556         PAUSE: 19,\r
557         /** Key constant @type Number */\r
558         CAPS_LOCK: 20,\r
559         /** Key constant @type Number */\r
560         ESC: 27,\r
561         /** Key constant @type Number */\r
562         SPACE: 32,\r
563         /** Key constant @type Number */\r
564         PAGE_UP: 33,\r
565         PAGEUP : 33, // legacy\r
566         /** Key constant @type Number */\r
567         PAGE_DOWN: 34,\r
568         PAGEDOWN : 34, // legacy\r
569         /** Key constant @type Number */\r
570         END: 35,\r
571         /** Key constant @type Number */\r
572         HOME: 36,\r
573         /** Key constant @type Number */\r
574         LEFT: 37,\r
575         /** Key constant @type Number */\r
576         UP: 38,\r
577         /** Key constant @type Number */\r
578         RIGHT: 39,\r
579         /** Key constant @type Number */\r
580         DOWN: 40,\r
581         /** Key constant @type Number */\r
582         PRINT_SCREEN: 44,\r
583         /** Key constant @type Number */\r
584         INSERT: 45,\r
585         /** Key constant @type Number */\r
586         DELETE: 46,\r
587         /** Key constant @type Number */\r
588         ZERO: 48,\r
589         /** Key constant @type Number */\r
590         ONE: 49,\r
591         /** Key constant @type Number */\r
592         TWO: 50,\r
593         /** Key constant @type Number */\r
594         THREE: 51,\r
595         /** Key constant @type Number */\r
596         FOUR: 52,\r
597         /** Key constant @type Number */\r
598         FIVE: 53,\r
599         /** Key constant @type Number */\r
600         SIX: 54,\r
601         /** Key constant @type Number */\r
602         SEVEN: 55,\r
603         /** Key constant @type Number */\r
604         EIGHT: 56,\r
605         /** Key constant @type Number */\r
606         NINE: 57,\r
607         /** Key constant @type Number */\r
608         A: 65,\r
609         /** Key constant @type Number */\r
610         B: 66,\r
611         /** Key constant @type Number */\r
612         C: 67,\r
613         /** Key constant @type Number */\r
614         D: 68,\r
615         /** Key constant @type Number */\r
616         E: 69,\r
617         /** Key constant @type Number */\r
618         F: 70,\r
619         /** Key constant @type Number */\r
620         G: 71,\r
621         /** Key constant @type Number */\r
622         H: 72,\r
623         /** Key constant @type Number */\r
624         I: 73,\r
625         /** Key constant @type Number */\r
626         J: 74,\r
627         /** Key constant @type Number */\r
628         K: 75,\r
629         /** Key constant @type Number */\r
630         L: 76,\r
631         /** Key constant @type Number */\r
632         M: 77,\r
633         /** Key constant @type Number */\r
634         N: 78,\r
635         /** Key constant @type Number */\r
636         O: 79,\r
637         /** Key constant @type Number */\r
638         P: 80,\r
639         /** Key constant @type Number */\r
640         Q: 81,\r
641         /** Key constant @type Number */\r
642         R: 82,\r
643         /** Key constant @type Number */\r
644         S: 83,\r
645         /** Key constant @type Number */\r
646         T: 84,\r
647         /** Key constant @type Number */\r
648         U: 85,\r
649         /** Key constant @type Number */\r
650         V: 86,\r
651         /** Key constant @type Number */\r
652         W: 87,\r
653         /** Key constant @type Number */\r
654         X: 88,\r
655         /** Key constant @type Number */\r
656         Y: 89,\r
657         /** Key constant @type Number */\r
658         Z: 90,\r
659         /** Key constant @type Number */\r
660         CONTEXT_MENU: 93,\r
661         /** Key constant @type Number */\r
662         NUM_ZERO: 96,\r
663         /** Key constant @type Number */\r
664         NUM_ONE: 97,\r
665         /** Key constant @type Number */\r
666         NUM_TWO: 98,\r
667         /** Key constant @type Number */\r
668         NUM_THREE: 99,\r
669         /** Key constant @type Number */\r
670         NUM_FOUR: 100,\r
671         /** Key constant @type Number */\r
672         NUM_FIVE: 101,\r
673         /** Key constant @type Number */\r
674         NUM_SIX: 102,\r
675         /** Key constant @type Number */\r
676         NUM_SEVEN: 103,\r
677         /** Key constant @type Number */\r
678         NUM_EIGHT: 104,\r
679         /** Key constant @type Number */\r
680         NUM_NINE: 105,\r
681         /** Key constant @type Number */\r
682         NUM_MULTIPLY: 106,\r
683         /** Key constant @type Number */\r
684         NUM_PLUS: 107,\r
685         /** Key constant @type Number */\r
686         NUM_MINUS: 109,\r
687         /** Key constant @type Number */\r
688         NUM_PERIOD: 110,\r
689         /** Key constant @type Number */\r
690         NUM_DIVISION: 111,\r
691         /** Key constant @type Number */\r
692         F1: 112,\r
693         /** Key constant @type Number */\r
694         F2: 113,\r
695         /** Key constant @type Number */\r
696         F3: 114,\r
697         /** Key constant @type Number */\r
698         F4: 115,\r
699         /** Key constant @type Number */\r
700         F5: 116,\r
701         /** Key constant @type Number */\r
702         F6: 117,\r
703         /** Key constant @type Number */\r
704         F7: 118,\r
705         /** Key constant @type Number */\r
706         F8: 119,\r
707         /** Key constant @type Number */\r
708         F9: 120,\r
709         /** Key constant @type Number */\r
710         F10: 121,\r
711         /** Key constant @type Number */\r
712         F11: 122,\r
713         /** Key constant @type Number */\r
714         F12: 123,\r
715 \r
716            /** @private */\r
717         setEvent : function(e){\r
718             if(e == this || (e && e.browserEvent)){ // already wrapped\r
719                 return e;\r
720             }\r
721             this.browserEvent = e;\r
722             if(e){\r
723                 // normalize buttons\r
724                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);\r
725                 if(e.type == 'click' && this.button == -1){\r
726                     this.button = 0;\r
727                 }\r
728                 this.type = e.type;\r
729                 this.shiftKey = e.shiftKey;\r
730                 // mac metaKey behaves like ctrlKey\r
731                 this.ctrlKey = e.ctrlKey || e.metaKey;\r
732                 this.altKey = e.altKey;\r
733                 // in getKey these will be normalized for the mac\r
734                 this.keyCode = e.keyCode;\r
735                 this.charCode = e.charCode;\r
736                 // cache the target for the delayed and or buffered events\r
737                 this.target = E.getTarget(e);\r
738                 // same for XY\r
739                 this.xy = E.getXY(e);\r
740             }else{\r
741                 this.button = -1;\r
742                 this.shiftKey = false;\r
743                 this.ctrlKey = false;\r
744                 this.altKey = false;\r
745                 this.keyCode = 0;\r
746                 this.charCode = 0;\r
747                 this.target = null;\r
748                 this.xy = [0, 0];\r
749             }\r
750             return this;\r
751         },\r
752 \r
753         /**\r
754          * Stop the event (preventDefault and stopPropagation)\r
755          */\r
756         stopEvent : function(){\r
757             if(this.browserEvent){\r
758                 if(this.browserEvent.type == 'mousedown'){\r
759                     Ext.EventManager.stoppedMouseDownEvent.fire(this);\r
760                 }\r
761                 E.stopEvent(this.browserEvent);\r
762             }\r
763         },\r
764 \r
765         /**\r
766          * Prevents the browsers default handling of the event.\r
767          */\r
768         preventDefault : function(){\r
769             if(this.browserEvent){\r
770                 E.preventDefault(this.browserEvent);\r
771             }\r
772         },\r
773 \r
774         /** @private */\r
775         isNavKeyPress : function(){\r
776             var k = this.keyCode;\r
777             k = Ext.isSafari ? (safariKeys[k] || k) : k;\r
778             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;\r
779         },\r
780 \r
781         isSpecialKey : function(){\r
782             var k = this.keyCode;\r
783             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||\r
784             (k == 16) || (k == 17) ||\r
785             (k >= 18 && k <= 20) ||\r
786             (k >= 33 && k <= 35) ||\r
787             (k >= 36 && k <= 39) ||\r
788             (k >= 44 && k <= 45);\r
789         },\r
790 \r
791         /**\r
792          * Cancels bubbling of the event.\r
793          */\r
794         stopPropagation : function(){\r
795             if(this.browserEvent){\r
796                 if(this.browserEvent.type == 'mousedown'){\r
797                     Ext.EventManager.stoppedMouseDownEvent.fire(this);\r
798                 }\r
799                 E.stopPropagation(this.browserEvent);\r
800             }\r
801         },\r
802 \r
803         /**\r
804          * Gets the character code for the event.\r
805          * @return {Number}\r
806          */\r
807         getCharCode : function(){\r
808             return this.charCode || this.keyCode;\r
809         },\r
810 \r
811         /**\r
812          * Returns a normalized keyCode for the event.\r
813          * @return {Number} The key code\r
814          */\r
815         getKey : function(){\r
816             var k = this.keyCode || this.charCode;\r
817             return Ext.isSafari ? (safariKeys[k] || k) : k;\r
818         },\r
819 \r
820         /**\r
821          * Gets the x coordinate of the event.\r
822          * @return {Number}\r
823          */\r
824         getPageX : function(){\r
825             return this.xy[0];\r
826         },\r
827 \r
828         /**\r
829          * Gets the y coordinate of the event.\r
830          * @return {Number}\r
831          */\r
832         getPageY : function(){\r
833             return this.xy[1];\r
834         },\r
835 \r
836         /**\r
837          * Gets the time of the event.\r
838          * @return {Number}\r
839          */\r
840         getTime : function(){\r
841             if(this.browserEvent){\r
842                 return E.getTime(this.browserEvent);\r
843             }\r
844             return null;\r
845         },\r
846 \r
847         /**\r
848          * Gets the page coordinates of the event.\r
849          * @return {Array} The xy values like [x, y]\r
850          */\r
851         getXY : function(){\r
852             return this.xy;\r
853         },\r
854 \r
855         /**\r
856          * Gets the target for the event.\r
857          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target\r
858          * @param {Number/Mixed} maxDepth (optional) The max depth to\r
859                 search as a number or element (defaults to 10 || document.body)\r
860          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node\r
861          * @return {HTMLelement}\r
862          */\r
863         getTarget : function(selector, maxDepth, returnEl){\r
864             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);\r
865         },\r
866 \r
867         /**\r
868          * Gets the related target.\r
869          * @return {HTMLElement}\r
870          */\r
871         getRelatedTarget : function(){\r
872             if(this.browserEvent){\r
873                 return E.getRelatedTarget(this.browserEvent);\r
874             }\r
875             return null;\r
876         },\r
877 \r
878         /**\r
879          * Normalizes mouse wheel delta across browsers\r
880          * @return {Number} The delta\r
881          */\r
882         getWheelDelta : function(){\r
883             var e = this.browserEvent;\r
884             var delta = 0;\r
885             if(e.wheelDelta){ /* IE/Opera. */\r
886                 delta = e.wheelDelta/120;\r
887             }else if(e.detail){ /* Mozilla case. */\r
888                 delta = -e.detail/3;\r
889             }\r
890             return delta;\r
891         },\r
892 \r
893         /**\r
894          * Returns true if the control, meta, shift or alt key was pressed during this event.\r
895          * @return {Boolean}\r
896          */\r
897         hasModifier : function(){\r
898             return ((this.ctrlKey || this.altKey) || this.shiftKey) ? true : false;\r
899         },\r
900 \r
901         /**\r
902          * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.\r
903          * Example usage:<pre><code>\r
904 // Handle click on any child of an element\r
905 Ext.getBody().on('click', function(e){\r
906     if(e.within('some-el')){\r
907         alert('Clicked on a child of some-el!');\r
908     }\r
909 });\r
910 \r
911 // Handle click directly on an element, ignoring clicks on child nodes\r
912 Ext.getBody().on('click', function(e,t){\r
913     if((t.id == 'some-el') && !e.within(t, true)){\r
914         alert('Clicked directly on some-el!');\r
915     }\r
916 });\r
917 </code></pre>\r
918          * @param {Mixed} el The id, DOM element or Ext.Element to check\r
919          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target\r
920          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target\r
921          * @return {Boolean}\r
922          */\r
923         within : function(el, related, allowEl){\r
924             var t = this[related ? "getRelatedTarget" : "getTarget"]();\r
925             return t && ((allowEl ? (t === Ext.getDom(el)) : false) || Ext.fly(el).contains(t));\r
926         },\r
927 \r
928         getPoint : function(){\r
929             return new Ext.lib.Point(this.xy[0], this.xy[1]);\r
930         }\r
931     };\r
932 \r
933     return new Ext.EventObjectImpl();\r
934 }();