/**
 * @class Ext.event.Event
 */
Ext.define('Ext.overrides.event.Event', {
    override: 'Ext.event.Event',
 
    // map of events that should fire global mousedown even if stopped
    mousedownEvents: {
        mousedown: 1,
        pointerdown: 1,
        touchstart: 1
    },
 
    /**
     * @method injectEvent
     * @member Ext.event.Event
     * Injects a DOM event using the data in this object and (optionally) a new target.
     * This is a low-level technique and not likely to be used by application code. The
     * currently supported event types are:
     * <p><b>HTMLEvents</b></p>
     * <ul>
     * <li>load</li>
     * <li>unload</li>
     * <li>select</li>
     * <li>change</li>
     * <li>submit</li>
     * <li>reset</li>
     * <li>resize</li>
     * <li>scroll</li>
     * </ul>
     * <p><b>MouseEvents</b></p>
     * <ul>
     * <li>click</li>
     * <li>dblclick</li>
     * <li>mousedown</li>
     * <li>mouseup</li>
     * <li>mouseover</li>
     * <li>mousemove</li>
     * <li>mouseout</li>
     * </ul>
     * <p><b>UIEvents</b></p>
     * <ul>
     * <li>focusin</li>
     * <li>focusout</li>
     * <li>activate</li>
     * <li>focus</li>
     * <li>blur</li>
     * </ul>
     * @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This
     * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
     * is used to determine the target.
     */
    injectEvent: (function () {
        var API,
            dispatchers = {}, // keyed by event type (e.g., 'mousedown')
            crazyIEButtons;
 
        // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
 
        // IE9 has createEvent, but this code causes major problems with htmleditor (it
        // blocks all mouse events and maybe more). TODO
 
        if (!Ext.isIE9m && document.createEvent) { // if (DOM compliant)
            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEvent('HTMLEvents');
 
                    event.initEvent(type, bubbles, cancelable);
                    return event;
                },
 
                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEvent('MouseEvents'),
                        view = doc.defaultView || window;
 
                    if (event.initMouseEvent) {
                        event.initMouseEvent(type, bubbles, cancelable, view, detail,
                                    clientX, clientY, clientX, clientY, ctrlKey, altKey,
                                    shiftKey, metaKey, button, relatedTarget);
                    } else { // old Safari
                        event = doc.createEvent('UIEvents');
                        event.initEvent(type, bubbles, cancelable);
                        event.view = view;
                        event.detail = detail;
                        event.screenX = clientX;
                        event.screenY = clientY;
                        event.clientX = clientX;
                        event.clientY = clientY;
                        event.ctrlKey = ctrlKey;
                        event.altKey = altKey;
                        event.metaKey = metaKey;
                        event.shiftKey = shiftKey;
                        event.button = button;
                        event.relatedTarget = relatedTarget;
                    }
 
                    return event;
                },
 
                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEvent('UIEvents'),
                        view = doc.defaultView || window;
 
                    event.initUIEvent(type, bubbles, cancelable, view, detail);
                    return event;
                },
 
                fireEvent: function (target, type, event) {
                    target.dispatchEvent(event);
                }
            };
        } else if (document.createEventObject) { // else if (IE)
            crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
 
            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },
 
                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    event.detail = detail;
                    event.screenX = clientX;
                    event.screenY = clientY;
                    event.clientX = clientX;
                    event.clientY = clientY;
                    event.ctrlKey = ctrlKey;
                    event.altKey = altKey;
                    event.shiftKey = shiftKey;
                    event.metaKey = metaKey;
                    event.button = crazyIEButtons[button] || button;
                    event.relatedTarget = relatedTarget; // cannot assign to/fromElement
                    return event;
                },
 
                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },
 
                fireEvent: function (target, type, event) {
                    target.fireEvent('on' + type, event);
                }
            };
        }
 
        //----------------
        // HTMLEvents
 
        Ext.Object.each({
                load:   [false, false],
                unload: [false, false],
                select: [true, false],
                change: [true, false],
                submit: [true, true],
                reset:  [true, false],
                resize: [true, false],
                scroll: [true, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createHtmlEvent(name, bubbles, cancelable);
                    API.fireEvent(targetEl, name, e);
                };
            });
 
        //----------------
        // MouseEvents
 
        function createMouseEventDispatcher (type, detail) {
            var cancelable = (type !== 'mousemove');
            return function (targetEl, srcEvent) {
                var xy = srcEvent.getXY(),
                    e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
                                detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
                                srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
                                srcEvent.relatedTarget);
                API.fireEvent(targetEl, type, e);
            };
        }
 
        Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
            function (eventName) {
                dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
            });
 
        //----------------
        // UIEvents
 
        Ext.Object.each({
                focusin:  [true, false],
                focusout: [true, false],
                activate: [true, true],
                focus:    [false, false],
                blur:     [false, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
                    API.fireEvent(targetEl, name, e);
                };
            });
 
        //---------
        if (!API) {
            // not even sure what ancient browsers fall into this category...
 
            dispatchers = {}; // never mind all those we just built :P
 
            API = {};
        }
 
        function cannotInject (target, srcEvent) {
            //<debug>
            // TODO log something
            //</debug>
        }
 
        return function (target) {
            var me = this,
                dispatcher = dispatchers[me.type] || cannotInject,
                t = target ? (target.dom || target) : me.getTarget();
 
            dispatcher(t, me);
        };
    }()), // call to produce method
 
    preventDefault: function(browserOnly) {
        var me = this,
            event = me.browserEvent,
            parentEvent = me.parentEvent,
            unselectable, target;
 
 
        // This check is for IE8/9. The event object may have been
        // invalidated, so we can't delve into the details of it. If so,
        // just fall out gracefully and don't attempt to do anything.
        if (typeof event.type !== 'unknown') {
            // In some cases we want to prevent default on the browser event
            // but keep propagating it through our event system. For example,
            // in Checkbox selection where the cells with checkboxes should
            // prevent focusing on mousedown but still fire the click event.
            if (!browserOnly) {
                me.defaultPrevented = true;
            }
 
            // if the event was created by prototype-chaining a new object to an existing event
            // instance, we need to make sure the parent event is defaultPrevented as well.
            if (parentEvent) {
                parentEvent.defaultPrevented = true;
            }
 
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                // The purpose of the code below is for preventDefault to stop focus from
                // occurring like it does in other modern browsers. This only happens in
                // IE8/9 when using attachEvent. The use of unselectable seems the most reliable
                // way to prevent this from happening. We need to use a timeout to restore the
                // unselectable state because if we don't setting it has no effect. It's important
                // to set the atrribute to 'on' as opposed to just setting the property on the DOM element.
                // See the link below for a discussion on the issue:
                // http://bugs.jquery.com/ticket/10345
                if (event.type === 'mousedown') {
                    target = event.target;
                    unselectable = target.getAttribute('unselectable');
                    if (unselectable !== 'on') {
                        target.setAttribute('unselectable', 'on');
                        Ext.defer(function() {
                            target.setAttribute('unselectable', unselectable);
                        }, 1);
                    }
                }
                // IE9 and earlier do not support preventDefault
                event.returnValue = false;
                // Some keys events require setting the keyCode to -1 to be prevented
                // all ctrl + X and F1 -> F12
                if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                    event.keyCode = -1;
                }
            }
        }
 
        return me;
    },
 
    stopPropagation: function() {
        var me = this,
            event = me.browserEvent;
 
        // This check is for IE8/9. The event object may have been
        // invalidated, so we can't delve into the details of it. If so,
        // just fall out gracefully and don't attempt to do anything.
        if (typeof event.type !== 'unknown') {
            if (me.mousedownEvents[me.type]) {
                // Fire the "unstoppable" global mousedown event
                // (used for menu hiding, etc)
                Ext.GlobalEvents.fireMouseDown(me);
            }
            me.callParent();
        }
        return me;
    },
 
    deprecated: {
        '5.0': {
            methods: {
                /**
                 * @method clone
                 * @member Ext.event.Event
                 * Clones this event.
                 * @return {Ext.event.Event} The cloned copy
                 * @deprecated 5.0.0
                 */
                clone: function() {
                    return new this.self(this.browserEvent, this);
                }
            }
        }
    }
}, function() {
    var Event = this,
        btnMap,
        onKeyDown = function(e) {
            if (e.keyCode === 9) {
                Event.forwardTab = !e.shiftKey;
            }
        },
        onKeyUp = function(e) {
            if (e.keyCode === 9) {
                delete Event.forwardTab;
            }
        };
 
//<feature legacyBrowser>
    if (Ext.isIE9m) {
        btnMap = {
            0: 0,
            1: 0,
            4: 1,
            2: 2
        };
 
        Event.override({
            statics: {
                /**
                 * @member Ext.event.Event
                 * When events are attached using IE's attachEvent API instead of
                 * addEventListener accessing any members of an event object asynchronously
                 * results in "Member not found" error.  To work around this we fabricate
                 * our own event object by copying all of its members to a new object.
                 * @param {Event} browserEvent The native browser event object
                 * @private
                 * @static
                 */
                enableIEAsync: function(browserEvent) {
                    var name,
                        fakeEvent = {};
 
                    for (name in browserEvent) {
                        fakeEvent[name] = browserEvent[name];
                    }
 
                    return fakeEvent;
                }
            },
 
            constructor: function(event, info, touchesMap, identifiers) {
                var me = this;
                me.callParent([event, info, touchesMap, identifiers]);
                me.button = btnMap[event.button];
 
                if (event.type === 'contextmenu') {
                    me.button = 2; // IE8/9 reports click as 0, so we can at least attempt to infer here
                }   
 
                // IE8 can throw an error when trying to access properties on a browserEvent
                // object when the event has been buffered or delayed.  Cache them here
                // so we can access them later.
                me.toElement = event.toElement;
                me.fromElement = event.fromElement;
            },
 
            mouseLeaveRe: /(mouseout|mouseleave)/,
            mouseEnterRe: /(mouseover|mouseenter)/,
 
            /**
             * @member Ext.event.Event
             * @inheritdoc Ext.event.Event#static-enableIEAsync
             * @private
             */
            enableIEAsync: function(browserEvent) {
                this.browserEvent = this.self.enableIEAsync(browserEvent);
            },
 
            getRelatedTarget: function(selector, maxDepth, returnEl) {
                var me = this,
                    type, target;
 
                if (!me.relatedTarget) {
                    type = me.type;
                    if (me.mouseLeaveRe.test(type)) {
                        target = me.toElement;
                    } else if (me.mouseEnterRe.test(type)) {
                        target = me.fromElement;
                    }
                    if (target) {
                        me.relatedTarget = me.self.resolveTextNode(target);
                    }
                }
 
                return me.callParent([selector, maxDepth, returnEl]);
            }
        });
 
        // We place these listeners to capture Tab and Shift-Tab key strokes
        // and pass this information in the focus/blur event if it happens
        // between keydown/keyup pair.
        document.attachEvent('onkeydown', onKeyDown);
        document.attachEvent('onkeyup',   onKeyUp);
        
        window.attachEvent('onunload', function() {
            document.detachEvent('onkeydown', onKeyDown);
            document.detachEvent('onkeyup',   onKeyUp);
        });
    }
    else
//</feature>
    if (document.addEventListener) {
        document.addEventListener('keydown', onKeyDown, true);
        document.addEventListener('keyup',   onKeyUp,   true);
    }
});