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