3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.EventObject
18 Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
19 wraps the browser's native event-object normalizing cross-browser differences,
20 such as which mouse button is clicked, keys pressed, mechanisms to stop
21 event-propagation along with a method to prevent default actions from taking place.
25 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
27 var target = e.getTarget(); // same as t (the target HTMLElement)
31 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
32 myDiv.on( // 'on' is shorthand for addListener
33 "click", // perform an action on click of myDiv
34 handleClick // reference to the action handler
37 // other methods to do the same:
38 Ext.EventManager.on("myDiv", 'click', handleClick);
39 Ext.EventManager.addListener("myDiv", 'click', handleClick);
44 Ext.define('Ext.EventObjectImpl', {
45 uses: ['Ext.util.Point'],
47 /** Key constant @type Number */
49 /** Key constant @type Number */
51 /** Key constant @type Number */
53 /** Key constant @type Number */
55 /** Key constant @type Number */
57 /** Key constant @type Number */
59 /** Key constant @type Number */
61 /** Key constant @type Number */
63 /** Key constant @type Number */
65 /** Key constant @type Number */
67 /** Key constant @type Number */
69 /** Key constant @type Number */
71 /** Key constant @type Number */
73 /** Key constant @type Number */
75 /** Key constant @type Number */
77 /** Key constant @type Number */
79 /** Key constant @type Number */
81 /** Key constant @type Number */
83 /** Key constant @type Number */
85 /** Key constant @type Number */
87 /** Key constant @type Number */
89 /** Key constant @type Number */
91 /** Key constant @type Number */
93 /** Key constant @type Number */
95 /** Key constant @type Number */
97 /** Key constant @type Number */
99 /** Key constant @type Number */
101 /** Key constant @type Number */
103 /** Key constant @type Number */
105 /** Key constant @type Number */
107 /** Key constant @type Number */
109 /** Key constant @type Number */
111 /** Key constant @type Number */
113 /** Key constant @type Number */
115 /** Key constant @type Number */
117 /** Key constant @type Number */
119 /** Key constant @type Number */
121 /** Key constant @type Number */
123 /** Key constant @type Number */
125 /** Key constant @type Number */
127 /** Key constant @type Number */
129 /** Key constant @type Number */
131 /** Key constant @type Number */
133 /** Key constant @type Number */
135 /** Key constant @type Number */
137 /** Key constant @type Number */
139 /** Key constant @type Number */
141 /** Key constant @type Number */
143 /** Key constant @type Number */
145 /** Key constant @type Number */
147 /** Key constant @type Number */
149 /** Key constant @type Number */
151 /** Key constant @type Number */
153 /** Key constant @type Number */
155 /** Key constant @type Number */
157 /** Key constant @type Number */
159 /** Key constant @type Number */
161 /** Key constant @type Number */
163 /** Key constant @type Number */
165 /** Key constant @type Number */
167 /** Key constant @type Number */
169 /** Key constant @type Number */
171 /** Key constant @type Number */
173 /** Key constant @type Number */
175 /** Key constant @type Number */
177 /** Key constant @type Number */
179 /** Key constant @type Number */
181 /** Key constant @type Number */
183 /** Key constant @type Number */
185 /** Key constant @type Number */
187 /** Key constant @type Number */
189 /** Key constant @type Number */
191 /** Key constant @type Number */
193 /** Key constant @type Number */
195 /** Key constant @type Number */
197 /** Key constant @type Number */
199 /** Key constant @type Number */
201 /** Key constant @type Number */
203 /** Key constant @type Number */
205 /** Key constant @type Number */
207 /** Key constant @type Number */
209 /** Key constant @type Number */
211 /** Key constant @type Number */
213 /** Key constant @type Number */
215 /** Key constant @type Number */
217 /** Key constant @type Number */
219 /** Key constant @type Number */
222 * The mouse wheel delta scaling factor. This value depends on browser version and OS and
223 * attempts to produce a similar scrolling experience across all platforms and browsers.
225 * To change this value:
227 * Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
232 WHEEL_SCALE: (function () {
236 // Firefox uses 3 on all platforms
238 } else if (Ext.isMac) {
239 // Continuous scrolling devices have momentum and produce much more scroll than
240 // discrete devices on the same OS and browser. To make things exciting, Safari
241 // (and not Chrome) changed from small values to 120 (like IE).
243 if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
244 // Safari changed the scrolling factor to match IE (for details see
245 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
246 // change was introduced was 532.0
247 // Detailed discussion:
248 // https://bugs.webkit.org/show_bug.cgi?id=29601
249 // http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
252 // MS optical wheel mouse produces multiples of 12 which is close enough
253 // to help tame the speed of the continuous mice...
257 // Momentum scrolling produces very fast scrolling, so increase the scale factor
258 // to help produce similar results cross platform. This could be even larger and
259 // it would help those mice, but other mice would become almost unusable as a
260 // result (since we cannot tell which device type is in use).
263 // IE, Opera and other Windows browsers use 120.
274 clickRe: /(dbl)?click/,
275 // safari keypress events for special keys return bad keycodes
282 63276: 33, // page up
283 63277: 34, // page down
288 // normalize button clicks, don't see any way to feature detect this.
299 constructor: function(event, freezeEvent){
301 this.setEvent(event.browserEvent || event, freezeEvent);
305 setEvent: function(event, freezeEvent){
306 var me = this, button, options;
308 if (event == me || (event && event.browserEvent)) { // already wrapped
311 me.browserEvent = event;
314 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
315 if (me.clickRe.test(event.type) && button == -1) {
321 shiftKey: event.shiftKey,
322 // mac metaKey behaves like ctrlKey
323 ctrlKey: event.ctrlKey || event.metaKey || false,
324 altKey: event.altKey,
325 // in getKey these will be normalized for the mac
326 keyCode: event.keyCode,
327 charCode: event.charCode,
328 // cache the targets for the delayed and or buffered events
329 target: Ext.EventManager.getTarget(event),
330 relatedTarget: Ext.EventManager.getRelatedTarget(event),
331 currentTarget: event.currentTarget,
332 xy: (freezeEvent ? me.getXY() : null)
346 Ext.apply(me, options);
351 * Stop the event (preventDefault and stopPropagation)
353 stopEvent: function(){
354 this.stopPropagation();
355 this.preventDefault();
359 * Prevents the browsers default handling of the event.
361 preventDefault: function(){
362 if (this.browserEvent) {
363 Ext.EventManager.preventDefault(this.browserEvent);
368 * Cancels bubbling of the event.
370 stopPropagation: function(){
371 var browserEvent = this.browserEvent;
374 if (browserEvent.type == 'mousedown') {
375 Ext.EventManager.stoppedMouseDownEvent.fire(this);
377 Ext.EventManager.stopPropagation(browserEvent);
382 * Gets the character code for the event.
385 getCharCode: function(){
386 return this.charCode || this.keyCode;
390 * Returns a normalized keyCode for the event.
391 * @return {Number} The key code
394 return this.normalizeKey(this.keyCode || this.charCode);
398 * Normalize key codes across browsers
400 * @param {Number} key The key code
401 * @return {Number} The normalized code
403 normalizeKey: function(key){
404 // can't feature detect this
405 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
409 * Gets the x coordinate of the event.
411 * @deprecated 4.0 Replaced by {@link #getX}
413 getPageX: function(){
418 * Gets the y coordinate of the event.
420 * @deprecated 4.0 Replaced by {@link #getY}
422 getPageY: function(){
427 * Gets the x coordinate of the event.
431 return this.getXY()[0];
435 * Gets the y coordinate of the event.
439 return this.getXY()[1];
443 * Gets the page coordinates of the event.
444 * @return {Number[]} The xy values like [x, y]
449 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
455 * Gets the target for the event.
456 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
457 * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
458 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
459 * @return {HTMLElement}
461 getTarget : function(selector, maxDepth, returnEl){
463 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
465 return returnEl ? Ext.get(this.target) : this.target;
469 * Gets the related target.
470 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
471 * @param {Number/HTMLElement} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
472 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
473 * @return {HTMLElement}
475 getRelatedTarget : function(selector, maxDepth, returnEl){
477 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
479 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
483 * Correctly scales a given wheel delta.
484 * @param {Number} delta The delta value.
486 correctWheelDelta : function (delta) {
487 var scale = this.WHEEL_SCALE,
488 ret = Math.round(delta / scale);
491 ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
498 * Returns the mouse wheel deltas for this event.
499 * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
501 getWheelDeltas : function () {
503 event = me.browserEvent,
504 dx = 0, dy = 0; // the deltas
506 if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
507 dx = event.wheelDeltaX;
508 dy = event.wheelDeltaY;
509 } else if (event.wheelDelta) { // old WebKit and IE
510 dy = event.wheelDelta;
511 } else if (event.detail) { // Gecko
512 dy = -event.detail; // gecko is backwards
514 // Gecko sometimes returns really big values if the user changes settings to
515 // scroll a whole page per scroll
518 } else if (dy < -100) {
522 // Firefox 3.1 adds an axis field to the event to indicate direction of
523 // scroll. See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
524 if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
531 x: me.correctWheelDelta(dx),
532 y: me.correctWheelDelta(dy)
537 * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
538 * {@link #getWheelDeltas} instead.
539 * @return {Number} The mouse wheel y-delta
541 getWheelDelta : function(){
542 var deltas = this.getWheelDeltas();
548 * 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.
549 * Example usage:<pre><code>
550 // Handle click on any child of an element
551 Ext.getBody().on('click', function(e){
552 if(e.within('some-el')){
553 alert('Clicked on a child of some-el!');
557 // Handle click directly on an element, ignoring clicks on child nodes
558 Ext.getBody().on('click', function(e,t){
559 if((t.id == 'some-el') && !e.within(t, true)){
560 alert('Clicked directly on some-el!');
564 * @param {String/HTMLElement/Ext.Element} el The id, DOM element or Ext.Element to check
565 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
566 * @param {Boolean} allowEl (optional) true to also check if the passed element is the target or related target
569 within : function(el, related, allowEl){
571 var t = related ? this.getRelatedTarget() : this.getTarget(),
575 result = Ext.fly(el).contains(t);
576 if (!result && allowEl) {
577 result = t == Ext.getDom(el);
586 * Checks if the key pressed was a "navigation" key
587 * @return {Boolean} True if the press is a navigation keypress
589 isNavKeyPress : function(){
591 k = this.normalizeKey(me.keyCode);
593 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
600 * Checks if the key pressed was a "special" key
601 * @return {Boolean} True if the press is a special keypress
603 isSpecialKey : function(){
604 var k = this.normalizeKey(this.keyCode);
605 return (this.type == 'keypress' && this.ctrlKey) ||
606 this.isNavKeyPress() ||
607 (k == this.BACKSPACE) || // Backspace
608 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
609 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
613 * Returns a point object that consists of the object coordinates.
614 * @return {Ext.util.Point} point
616 getPoint : function(){
617 var xy = this.getXY();
618 return Ext.create('Ext.util.Point', xy[0], xy[1]);
622 * Returns true if the control, meta, shift or alt key was pressed during this event.
625 hasModifier : function(){
626 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
630 * Injects a DOM event using the data in this object and (optionally) a new target.
631 * This is a low-level technique and not likely to be used by application code. The
632 * currently supported event types are:
633 * <p><b>HTMLEvents</b></p>
644 * <p><b>MouseEvents</b></p>
654 * <p><b>UIEvents</b></p>
662 * @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This
663 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
664 * is used to determine the target.
666 injectEvent: function () {
668 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
670 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
672 // IE9 has createEvent, but this code causes major problems with htmleditor (it
673 // blocks all mouse events and maybe more). TODO
675 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
677 createHtmlEvent: function (doc, type, bubbles, cancelable) {
678 var event = doc.createEvent('HTMLEvents');
680 event.initEvent(type, bubbles, cancelable);
684 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
685 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
686 button, relatedTarget) {
687 var event = doc.createEvent('MouseEvents'),
688 view = doc.defaultView || window;
690 if (event.initMouseEvent) {
691 event.initMouseEvent(type, bubbles, cancelable, view, detail,
692 clientX, clientY, clientX, clientY, ctrlKey, altKey,
693 shiftKey, metaKey, button, relatedTarget);
694 } else { // old Safari
695 event = doc.createEvent('UIEvents');
696 event.initEvent(type, bubbles, cancelable);
698 event.detail = detail;
699 event.screenX = clientX;
700 event.screenY = clientY;
701 event.clientX = clientX;
702 event.clientY = clientY;
703 event.ctrlKey = ctrlKey;
704 event.altKey = altKey;
705 event.metaKey = metaKey;
706 event.shiftKey = shiftKey;
707 event.button = button;
708 event.relatedTarget = relatedTarget;
714 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
715 var event = doc.createEvent('UIEvents'),
716 view = doc.defaultView || window;
718 event.initUIEvent(type, bubbles, cancelable, view, detail);
722 fireEvent: function (target, type, event) {
723 target.dispatchEvent(event);
726 fixTarget: function (target) {
727 // Safari3 doesn't have window.dispatchEvent()
728 if (target == window && !target.dispatchEvent) {
735 } else if (document.createEventObject) { // else if (IE)
736 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
739 createHtmlEvent: function (doc, type, bubbles, cancelable) {
740 var event = doc.createEventObject();
741 event.bubbles = bubbles;
742 event.cancelable = cancelable;
746 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
747 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
748 button, relatedTarget) {
749 var event = doc.createEventObject();
750 event.bubbles = bubbles;
751 event.cancelable = cancelable;
752 event.detail = detail;
753 event.screenX = clientX;
754 event.screenY = clientY;
755 event.clientX = clientX;
756 event.clientY = clientY;
757 event.ctrlKey = ctrlKey;
758 event.altKey = altKey;
759 event.shiftKey = shiftKey;
760 event.metaKey = metaKey;
761 event.button = crazyIEButtons[button] || button;
762 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
766 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
767 var event = doc.createEventObject();
768 event.bubbles = bubbles;
769 event.cancelable = cancelable;
773 fireEvent: function (target, type, event) {
774 target.fireEvent('on' + type, event);
777 fixTarget: function (target) {
778 if (target == document) {
779 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
780 // IE6,IE7 cannot properly call document.fireEvent()
781 return document.documentElement;
793 load: [false, false],
794 unload: [false, false],
795 select: [true, false],
796 change: [true, false],
797 submit: [true, true],
798 reset: [true, false],
799 resize: [true, false],
800 scroll: [true, false]
802 function (name, value) {
803 var bubbles = value[0], cancelable = value[1];
804 dispatchers[name] = function (targetEl, srcEvent) {
805 var e = API.createHtmlEvent(name, bubbles, cancelable);
806 API.fireEvent(targetEl, name, e);
813 function createMouseEventDispatcher (type, detail) {
814 var cancelable = (type != 'mousemove');
815 return function (targetEl, srcEvent) {
816 var xy = srcEvent.getXY(),
817 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
818 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
819 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
820 srcEvent.relatedTarget);
821 API.fireEvent(targetEl, type, e);
825 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
826 function (eventName) {
827 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
834 focusin: [true, false],
835 focusout: [true, false],
836 activate: [true, true],
837 focus: [false, false],
840 function (name, value) {
841 var bubbles = value[0], cancelable = value[1];
842 dispatchers[name] = function (targetEl, srcEvent) {
843 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
844 API.fireEvent(targetEl, name, e);
850 // not even sure what ancient browsers fall into this category...
852 dispatchers = {}; // never mind all those we just built :P
855 fixTarget: function (t) {
861 function cannotInject (target, srcEvent) {
863 // TODO log something
867 return function (target) {
869 dispatcher = dispatchers[me.type] || cannotInject,
870 t = target ? (target.dom || target) : me.getTarget();
872 t = API.fixTarget(t);
875 }() // call to produce method
879 Ext.EventObject = new Ext.EventObjectImpl();