Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / core / src / EventObject.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.EventObject
17
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.
22
23 For example:
24
25     function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
26         e.preventDefault();
27         var target = e.getTarget(); // same as t (the target HTMLElement)
28         ...
29     }
30
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
35     );
36
37     // other methods to do the same:
38     Ext.EventManager.on("myDiv", 'click', handleClick);
39     Ext.EventManager.addListener("myDiv", 'click', handleClick);
40
41  * @singleton
42  * @markdown
43  */
44 Ext.define('Ext.EventObjectImpl', {
45     uses: ['Ext.util.Point'],
46
47     /** Key constant @type Number */
48     BACKSPACE: 8,
49     /** Key constant @type Number */
50     TAB: 9,
51     /** Key constant @type Number */
52     NUM_CENTER: 12,
53     /** Key constant @type Number */
54     ENTER: 13,
55     /** Key constant @type Number */
56     RETURN: 13,
57     /** Key constant @type Number */
58     SHIFT: 16,
59     /** Key constant @type Number */
60     CTRL: 17,
61     /** Key constant @type Number */
62     ALT: 18,
63     /** Key constant @type Number */
64     PAUSE: 19,
65     /** Key constant @type Number */
66     CAPS_LOCK: 20,
67     /** Key constant @type Number */
68     ESC: 27,
69     /** Key constant @type Number */
70     SPACE: 32,
71     /** Key constant @type Number */
72     PAGE_UP: 33,
73     /** Key constant @type Number */
74     PAGE_DOWN: 34,
75     /** Key constant @type Number */
76     END: 35,
77     /** Key constant @type Number */
78     HOME: 36,
79     /** Key constant @type Number */
80     LEFT: 37,
81     /** Key constant @type Number */
82     UP: 38,
83     /** Key constant @type Number */
84     RIGHT: 39,
85     /** Key constant @type Number */
86     DOWN: 40,
87     /** Key constant @type Number */
88     PRINT_SCREEN: 44,
89     /** Key constant @type Number */
90     INSERT: 45,
91     /** Key constant @type Number */
92     DELETE: 46,
93     /** Key constant @type Number */
94     ZERO: 48,
95     /** Key constant @type Number */
96     ONE: 49,
97     /** Key constant @type Number */
98     TWO: 50,
99     /** Key constant @type Number */
100     THREE: 51,
101     /** Key constant @type Number */
102     FOUR: 52,
103     /** Key constant @type Number */
104     FIVE: 53,
105     /** Key constant @type Number */
106     SIX: 54,
107     /** Key constant @type Number */
108     SEVEN: 55,
109     /** Key constant @type Number */
110     EIGHT: 56,
111     /** Key constant @type Number */
112     NINE: 57,
113     /** Key constant @type Number */
114     A: 65,
115     /** Key constant @type Number */
116     B: 66,
117     /** Key constant @type Number */
118     C: 67,
119     /** Key constant @type Number */
120     D: 68,
121     /** Key constant @type Number */
122     E: 69,
123     /** Key constant @type Number */
124     F: 70,
125     /** Key constant @type Number */
126     G: 71,
127     /** Key constant @type Number */
128     H: 72,
129     /** Key constant @type Number */
130     I: 73,
131     /** Key constant @type Number */
132     J: 74,
133     /** Key constant @type Number */
134     K: 75,
135     /** Key constant @type Number */
136     L: 76,
137     /** Key constant @type Number */
138     M: 77,
139     /** Key constant @type Number */
140     N: 78,
141     /** Key constant @type Number */
142     O: 79,
143     /** Key constant @type Number */
144     P: 80,
145     /** Key constant @type Number */
146     Q: 81,
147     /** Key constant @type Number */
148     R: 82,
149     /** Key constant @type Number */
150     S: 83,
151     /** Key constant @type Number */
152     T: 84,
153     /** Key constant @type Number */
154     U: 85,
155     /** Key constant @type Number */
156     V: 86,
157     /** Key constant @type Number */
158     W: 87,
159     /** Key constant @type Number */
160     X: 88,
161     /** Key constant @type Number */
162     Y: 89,
163     /** Key constant @type Number */
164     Z: 90,
165     /** Key constant @type Number */
166     CONTEXT_MENU: 93,
167     /** Key constant @type Number */
168     NUM_ZERO: 96,
169     /** Key constant @type Number */
170     NUM_ONE: 97,
171     /** Key constant @type Number */
172     NUM_TWO: 98,
173     /** Key constant @type Number */
174     NUM_THREE: 99,
175     /** Key constant @type Number */
176     NUM_FOUR: 100,
177     /** Key constant @type Number */
178     NUM_FIVE: 101,
179     /** Key constant @type Number */
180     NUM_SIX: 102,
181     /** Key constant @type Number */
182     NUM_SEVEN: 103,
183     /** Key constant @type Number */
184     NUM_EIGHT: 104,
185     /** Key constant @type Number */
186     NUM_NINE: 105,
187     /** Key constant @type Number */
188     NUM_MULTIPLY: 106,
189     /** Key constant @type Number */
190     NUM_PLUS: 107,
191     /** Key constant @type Number */
192     NUM_MINUS: 109,
193     /** Key constant @type Number */
194     NUM_PERIOD: 110,
195     /** Key constant @type Number */
196     NUM_DIVISION: 111,
197     /** Key constant @type Number */
198     F1: 112,
199     /** Key constant @type Number */
200     F2: 113,
201     /** Key constant @type Number */
202     F3: 114,
203     /** Key constant @type Number */
204     F4: 115,
205     /** Key constant @type Number */
206     F5: 116,
207     /** Key constant @type Number */
208     F6: 117,
209     /** Key constant @type Number */
210     F7: 118,
211     /** Key constant @type Number */
212     F8: 119,
213     /** Key constant @type Number */
214     F9: 120,
215     /** Key constant @type Number */
216     F10: 121,
217     /** Key constant @type Number */
218     F11: 122,
219     /** Key constant @type Number */
220     F12: 123,
221     /**
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.
224      *
225      * To change this value:
226      *
227      *      Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
228      *
229      * @type Number
230      * @markdown
231      */
232     WHEEL_SCALE: (function () {
233         var scale;
234
235         if (Ext.isGecko) {
236             // Firefox uses 3 on all platforms
237             scale = 3;
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).
242
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
250                 scale = 120;
251             } else {
252                 // MS optical wheel mouse produces multiples of 12 which is close enough
253                 // to help tame the speed of the continuous mice...
254                 scale = 12;
255             }
256
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).
261             scale *= 3;
262         } else {
263             // IE, Opera and other Windows browsers use 120.
264             scale = 120;
265         }
266
267         return scale;
268     })(),
269
270     /**
271      * Simple click regex
272      * @private
273      */
274     clickRe: /(dbl)?click/,
275     // safari keypress events for special keys return bad keycodes
276     safariKeys: {
277         3: 13, // enter
278         63234: 37, // left
279         63235: 39, // right
280         63232: 38, // up
281         63233: 40, // down
282         63276: 33, // page up
283         63277: 34, // page down
284         63272: 46, // delete
285         63273: 36, // home
286         63275: 35 // end
287     },
288     // normalize button clicks, don't see any way to feature detect this.
289     btnMap: Ext.isIE ? {
290         1: 0,
291         4: 1,
292         2: 2
293     } : {
294         0: 0,
295         1: 1,
296         2: 2
297     },
298
299     constructor: function(event, freezeEvent){
300         if (event) {
301             this.setEvent(event.browserEvent || event, freezeEvent);
302         }
303     },
304
305     setEvent: function(event, freezeEvent){
306         var me = this, button, options;
307
308         if (event == me || (event && event.browserEvent)) { // already wrapped
309             return event;
310         }
311         me.browserEvent = event;
312         if (event) {
313             // normalize buttons
314             button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
315             if (me.clickRe.test(event.type) && button == -1) {
316                 button = 0;
317             }
318             options = {
319                 type: event.type,
320                 button: button,
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)
333             };
334         } else {
335             options = {
336                 button: -1,
337                 shiftKey: false,
338                 ctrlKey: false,
339                 altKey: false,
340                 keyCode: 0,
341                 charCode: 0,
342                 target: null,
343                 xy: [0, 0]
344             };
345         }
346         Ext.apply(me, options);
347         return me;
348     },
349
350     /**
351      * Stop the event (preventDefault and stopPropagation)
352      */
353     stopEvent: function(){
354         this.stopPropagation();
355         this.preventDefault();
356     },
357
358     /**
359      * Prevents the browsers default handling of the event.
360      */
361     preventDefault: function(){
362         if (this.browserEvent) {
363             Ext.EventManager.preventDefault(this.browserEvent);
364         }
365     },
366
367     /**
368      * Cancels bubbling of the event.
369      */
370     stopPropagation: function(){
371         var browserEvent = this.browserEvent;
372
373         if (browserEvent) {
374             if (browserEvent.type == 'mousedown') {
375                 Ext.EventManager.stoppedMouseDownEvent.fire(this);
376             }
377             Ext.EventManager.stopPropagation(browserEvent);
378         }
379     },
380
381     /**
382      * Gets the character code for the event.
383      * @return {Number}
384      */
385     getCharCode: function(){
386         return this.charCode || this.keyCode;
387     },
388
389     /**
390      * Returns a normalized keyCode for the event.
391      * @return {Number} The key code
392      */
393     getKey: function(){
394         return this.normalizeKey(this.keyCode || this.charCode);
395     },
396
397     /**
398      * Normalize key codes across browsers
399      * @private
400      * @param {Number} key The key code
401      * @return {Number} The normalized code
402      */
403     normalizeKey: function(key){
404         // can't feature detect this
405         return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
406     },
407
408     /**
409      * Gets the x coordinate of the event.
410      * @return {Number}
411      * @deprecated 4.0 Replaced by {@link #getX}
412      */
413     getPageX: function(){
414         return this.getX();
415     },
416
417     /**
418      * Gets the y coordinate of the event.
419      * @return {Number}
420      * @deprecated 4.0 Replaced by {@link #getY}
421      */
422     getPageY: function(){
423         return this.getY();
424     },
425
426     /**
427      * Gets the x coordinate of the event.
428      * @return {Number}
429      */
430     getX: function() {
431         return this.getXY()[0];
432     },
433
434     /**
435      * Gets the y coordinate of the event.
436      * @return {Number}
437      */
438     getY: function() {
439         return this.getXY()[1];
440     },
441
442     /**
443      * Gets the page coordinates of the event.
444      * @return {Number[]} The xy values like [x, y]
445      */
446     getXY: function() {
447         if (!this.xy) {
448             // same for XY
449             this.xy = Ext.EventManager.getPageXY(this.browserEvent);
450         }
451         return this.xy;
452     },
453
454     /**
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}
460      */
461     getTarget : function(selector, maxDepth, returnEl){
462         if (selector) {
463             return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
464         }
465         return returnEl ? Ext.get(this.target) : this.target;
466     },
467
468     /**
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}
474      */
475     getRelatedTarget : function(selector, maxDepth, returnEl){
476         if (selector) {
477             return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
478         }
479         return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
480     },
481
482     /**
483      * Correctly scales a given wheel delta.
484      * @param {Number} delta The delta value.
485      */
486     correctWheelDelta : function (delta) {
487         var scale = this.WHEEL_SCALE,
488             ret = Math.round(delta / scale);
489
490         if (!ret && delta) {
491             ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
492         }
493
494         return ret;
495     },
496
497     /**
498      * Returns the mouse wheel deltas for this event.
499      * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
500      */
501     getWheelDeltas : function () {
502         var me = this,
503             event = me.browserEvent,
504             dx = 0, dy = 0; // the deltas
505
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
513
514             // Gecko sometimes returns really big values if the user changes settings to
515             // scroll a whole page per scroll
516             if (dy > 100) {
517                 dy = 3;
518             } else if (dy < -100) {
519                 dy = -3;
520             }
521
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) {
525                 dx = dy;
526                 dy = 0;
527             }
528         }
529
530         return {
531             x: me.correctWheelDelta(dx),
532             y: me.correctWheelDelta(dy)
533         };
534     },
535
536     /**
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
540      */
541     getWheelDelta : function(){
542         var deltas = this.getWheelDeltas();
543
544         return deltas.y;
545     },
546
547     /**
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!');
554     }
555 });
556
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!');
561     }
562 });
563 </code></pre>
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
567      * @return {Boolean}
568      */
569     within : function(el, related, allowEl){
570         if(el){
571             var t = related ? this.getRelatedTarget() : this.getTarget(),
572                 result;
573
574             if (t) {
575                 result = Ext.fly(el).contains(t);
576                 if (!result && allowEl) {
577                     result = t == Ext.getDom(el);
578                 }
579                 return result;
580             }
581         }
582         return false;
583     },
584
585     /**
586      * Checks if the key pressed was a "navigation" key
587      * @return {Boolean} True if the press is a navigation keypress
588      */
589     isNavKeyPress : function(){
590         var me = this,
591             k = this.normalizeKey(me.keyCode);
592
593        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
594        k == me.RETURN ||
595        k == me.TAB ||
596        k == me.ESC;
597     },
598
599     /**
600      * Checks if the key pressed was a "special" key
601      * @return {Boolean} True if the press is a special keypress
602      */
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
610     },
611
612     /**
613      * Returns a point object that consists of the object coordinates.
614      * @return {Ext.util.Point} point
615      */
616     getPoint : function(){
617         var xy = this.getXY();
618         return Ext.create('Ext.util.Point', xy[0], xy[1]);
619     },
620
621    /**
622     * Returns true if the control, meta, shift or alt key was pressed during this event.
623     * @return {Boolean}
624     */
625     hasModifier : function(){
626         return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
627     },
628
629     /**
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>
634      * <ul>
635      * <li>load</li>
636      * <li>unload</li>
637      * <li>select</li>
638      * <li>change</li>
639      * <li>submit</li>
640      * <li>reset</li>
641      * <li>resize</li>
642      * <li>scroll</li>
643      * </ul>
644      * <p><b>MouseEvents</b></p>
645      * <ul>
646      * <li>click</li>
647      * <li>dblclick</li>
648      * <li>mousedown</li>
649      * <li>mouseup</li>
650      * <li>mouseover</li>
651      * <li>mousemove</li>
652      * <li>mouseout</li>
653      * </ul>
654      * <p><b>UIEvents</b></p>
655      * <ul>
656      * <li>focusin</li>
657      * <li>focusout</li>
658      * <li>activate</li>
659      * <li>focus</li>
660      * <li>blur</li>
661      * </ul>
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.
665      */
666     injectEvent: function () {
667         var API,
668             dispatchers = {}; // keyed by event type (e.g., 'mousedown')
669
670         // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
671
672         // IE9 has createEvent, but this code causes major problems with htmleditor (it
673         // blocks all mouse events and maybe more). TODO
674
675         if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
676             API = {
677                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
678                     var event = doc.createEvent('HTMLEvents');
679
680                     event.initEvent(type, bubbles, cancelable);
681                     return event;
682                 },
683
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;
689
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);
697                         event.view = view;
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;
709                     }
710
711                     return event;
712                 },
713
714                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
715                     var event = doc.createEvent('UIEvents'),
716                         view = doc.defaultView || window;
717
718                     event.initUIEvent(type, bubbles, cancelable, view, detail);
719                     return event;
720                 },
721
722                 fireEvent: function (target, type, event) {
723                     target.dispatchEvent(event);
724                 },
725
726                 fixTarget: function (target) {
727                     // Safari3 doesn't have window.dispatchEvent()
728                     if (target == window && !target.dispatchEvent) {
729                         return document;
730                     }
731
732                     return target;
733                 }
734             };
735         } else if (document.createEventObject) { // else if (IE)
736             var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
737
738             API = {
739                 createHtmlEvent: function (doc, type, bubbles, cancelable) {
740                     var event = doc.createEventObject();
741                     event.bubbles = bubbles;
742                     event.cancelable = cancelable;
743                     return event;
744                 },
745
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
763                     return event;
764                 },
765
766                 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
767                     var event = doc.createEventObject();
768                     event.bubbles = bubbles;
769                     event.cancelable = cancelable;
770                     return event;
771                 },
772
773                 fireEvent: function (target, type, event) {
774                     target.fireEvent('on' + type, event);
775                 },
776
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;
782                     }
783
784                     return target;
785                 }
786             };
787         }
788
789         //----------------
790         // HTMLEvents
791
792         Ext.Object.each({
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]
801             },
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);
807                 };
808             });
809
810         //----------------
811         // MouseEvents
812
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);
822             };
823         }
824
825         Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
826             function (eventName) {
827                 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
828             });
829
830         //----------------
831         // UIEvents
832
833         Ext.Object.each({
834                 focusin:  [true, false],
835                 focusout: [true, false],
836                 activate: [true, true],
837                 focus:    [false, false],
838                 blur:     [false, false]
839             },
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);
845                 };
846             });
847
848         //---------
849         if (!API) {
850             // not even sure what ancient browsers fall into this category...
851
852             dispatchers = {}; // never mind all those we just built :P
853
854             API = {
855                 fixTarget: function (t) {
856                     return t;
857                 }
858             };
859         }
860
861         function cannotInject (target, srcEvent) {
862             //<debug>
863             // TODO log something
864             //</debug>
865         }
866
867         return function (target) {
868             var me = this,
869                 dispatcher = dispatchers[me.type] || cannotInject,
870                 t = target ? (target.dom || target) : me.getTarget();
871
872             t = API.fixTarget(t);
873             dispatcher(t, me);
874         };
875     }() // call to produce method
876
877 }, function() {
878
879 Ext.EventObject = new Ext.EventObjectImpl();
880
881 });
882
883