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