/**
 * @class Ext.dom.Element
 * @alternateClassName Ext.Element
 * @mixins Ext.util.Positionable
 * @mixins Ext.mixin.Observable
 *
 * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.
 *
 * **Note:** The events included in this Class are the ones we've found to be the most commonly used. Many events are
 * not listed here due to the expedient rate of change across browsers. For a more comprehensive list, please visit the
 * following resources:
 *
 * + [Mozilla Event Reference Guide](https://developer.mozilla.org/en-US/docs/Web/Events)
 * + [W3 Pointer Events](http://www.w3.org/TR/pointerevents/)
 * + [W3 Touch Events](http://www.w3.org/TR/touch-events/)
 * + [W3 DOM 2 Events](http://www.w3.org/TR/DOM-Level-2-Events/)
 * + [W3 DOM 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/)
 *
 * ## Usage
 *
 *     // by id
 *     var el = Ext.get("my-div");
 *
 *     // by DOM element reference
 *     var el = Ext.get(myDivElement);
 *
 * ## Selecting Descendant Elements
 *
 * Ext.dom.Element instances can be used to select descendant nodes using CSS selectors.
 * There are 3 methods that can be used for this purpose, each with a slightly different
 * twist:
 *
 * - {@link #method-query}
 * - {@link #method-selectNode}
 * - {@link #method-select}
 *
 * These methods can accept any valid CSS selector since they all use
 * [querySelectorAll](http://www.w3.org/TR/css3-selectors/) under the hood. The primary
 * difference between these three methods is their return type:
 *
 * To get an array of HTMLElement instances matching the selector '.foo' use the query
 * method:
 *
 *     element.query('.foo');
 *
 * This can easily be transformed into an array of Ext.dom.Element instances by setting
 * the `asDom` parameter to `false`:
 *
 *     element.query('.foo', false);
 *
 * If the desired result is only the first matching HTMLElement use the selectNode method:
 *
 *     element.selectNode('.foo');
 *
 * Once again, the dom node can be wrapped in an Ext.dom.Element by setting the `asDom`
 * parameter to `false`:
 *
 *     element.selectNode('.foo', false);
 *
 * The `select` method is used when the desired return type is a {@link
 * Ext.CompositeElementLite CompositeElementLite} or a {@link Ext.CompositeElement
 * CompositeElement}.  These are collections of elements that can be operated on as a
 * group using any of the methods of Ext.dom.Element.  The only difference between the two
 * is that CompositeElementLite is a collection of HTMLElement instances, while
 * CompositeElement is a collection of Ext.dom.Element instances.  To retrieve a
 * CompositeElementLite that represents a collection of HTMLElements for selector '.foo':
 *
 *     element.select('.foo');
 *
 * For a {@link Ext.CompositeElement CompositeElement} simply pass `true` as the
 * `composite` parameter:
 *
 *     element.select('.foo', true);
 *
 * The query selection methods can be used even if you don't have a Ext.dom.Element to
 * start with For example to select an array of all HTMLElements in the document that match the
 * selector '.foo', simply wrap the document object in an Ext.dom.Element instance using
 * {@link Ext#fly}:
 *
 *     Ext.fly(document).query('.foo');
 *
 * # Animations
 *
 * When an element is manipulated, by default there is no animation.
 *
 *     var el = Ext.get("my-div");
 *
 *     // no animation
 *     el.setWidth(100);
 *
 * specified as boolean (true) for default animation effects.
 *
 *     // default animation
 *     el.setWidth(100, true);
 *
 * To configure the effects, an object literal with animation options to use as the Element animation configuration
 * object can also be specified. Note that the supported Element animation configuration options are a subset of the
 * {@link Ext.fx.Anim} animation options specific to Fx effects. The supported Element animation configuration options
 * are:
 *
 *     Option    Default   Description
 *     --------- --------  ---------------------------------------------
 *     duration  350       The duration of the animation in milliseconds
 *     easing    easeOut   The easing method
 *     callback  none      A function to execute when the anim completes
 *     scope     this      The scope (this) of the callback function
 *
 * Usage:
 *
 *     // Element animation options object
 *     var opt = {
 *         duration: 1000,
 *         easing: 'elasticIn',
 *         callback: this.foo,
 *         scope: this
 *     };
 *     // animation with some options set
 *     el.setWidth(100, opt);
 *
 * The Element animation object being used for the animation will be set on the options object as "anim", which allows
 * you to stop or manipulate the animation. Here is an example:
 *
 *     // using the "anim" property to get the Anim object
 *     if(opt.anim.isAnimated()){
 *         opt.anim.stop();
 *     }
 */
Ext.define('Ext.dom.Element', function(Element) {
    var WIN = window,
        DOC = document,
        docEl = DOC.documentElement,
        WIN_TOP = WIN.top,
        EMPTY = [],
        elementIdCounter,
        windowId,
        documentId,
        WIDTH = 'width',
        HEIGHT = 'height',
        MIN_WIDTH = 'min-width',
        MIN_HEIGHT = 'min-height',
        MAX_WIDTH = 'max-width',
        MAX_HEIGHT = 'max-height',
        TOP = 'top',
        RIGHT = 'right',
        BOTTOM = 'bottom',
        LEFT = 'left',
        VISIBILITY = 'visibility',
        HIDDEN = 'hidden',
        DISPLAY = "display",
        NONE = "none",
        ZINDEX = "z-index",
        POSITION = "position",
        RELATIVE = "relative",
        STATIC = "static",
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        classNameSplitRegex = /[\s]+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        endsQuestionRe = /\?$/,
        topRe = /top/i,
        empty = {},
        borders = {
            t: 'border-top-width',
            r: 'border-right-width',
            b: 'border-bottom-width',
            l: 'border-left-width'
        },
        paddings = {
            t: 'padding-top',
            r: 'padding-right',
            b: 'padding-bottom',
            l: 'padding-left'
        },
        margins = {
            t: 'margin-top',
            r: 'margin-right',
            b: 'margin-bottom',
            l: 'margin-left'
        },
        selectDir = {
            b: 'backward',
            back: 'backward',
            f: 'forward'
        },
        paddingsTLRB = [paddings.l, paddings.r, paddings.t, paddings.b],
        bordersTLRB = [borders.l,  borders.r,  borders.t,  borders.b],
        numberRe = /\d+$/,
        unitRe = /\d+(px|r?em|%|vh|vw|vmin|vmax|en|ch|ex|pt|in|cm|mm|pc)$/i,
        defaultUnit = 'px',
        camelRe = /(-[a-z])/gi,
        cssRe = /([a-z0-9\-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
        pxRe = /^\d+(?:\.\d*)?px$/i,
        relativeUnitRe = /(%|r?em|auto|vh|vw|vmin|vmax|ch|ex)$/i,
 
        propertyCache = {},
        ORIGINALDISPLAY = 'originalDisplay',
        camelReplaceFn = function(m, a) {
            return a.charAt(1).toUpperCase();
        },
 
        clearData = function(node, deep) {
            var childNodes, i, len;
 
            // Only Element nodes may have _extData and child nodes to clear. 
            // IE8 throws an error attempting to set expandos on non-Element nodes. 
            if (node.nodeType === 1) {
                node._extData = null;
                if (deep) {
                    childNodes = node.childNodes;
                    for (= 0, len = childNodes.length; i < len; ++i) {
                        clearData(childNodes[i], deep);
                    }
                }
            }
        },
 
        opacityCls = Ext.baseCSSPrefix + 'hidden-opacity',
        visibilityCls = Ext.baseCSSPrefix + 'hidden-visibility',
        displayCls = Ext.baseCSSPrefix + 'hidden-display',
        offsetsCls = Ext.baseCSSPrefix + 'hidden-offsets',
        clipCls = Ext.baseCSSPrefix + 'hidden-clip',
        lastFocusChange = 0,
        lastKeyboardClose = 0,
        editableHasFocus = false,
        isVirtualKeyboardOpen = false,
        inputTypeSelectionSupported = /text|password|search|tel|url/i,
        visFly, scrollFly, caFly, wrapFly, grannyFly, activeElFly;
 
    // Cross-origin access might throw an exception 
    try {
        elementIdCounter = WIN_TOP.__elementIdCounter__;
    }
    catch (e) {
        WIN_TOP = WIN;
    }
 
    WIN_TOP.__elementIdCounter = elementIdCounter = (WIN_TOP.__elementIdCounter__ || 0) + 1;
    windowId = 'ext-window-' + elementIdCounter;
    documentId = 'ext-document-' + elementIdCounter;
 
    //<debug> 
    if (Object.freeze) {
        Object.freeze(EMPTY);
    }
    //</debug> 
 
    return {
        alternateClassName: [ 'Ext.Element' ],
 
        mixins: [
            'Ext.util.Positionable',
            'Ext.mixin.Observable'
        ],
 
        requires: [
            'Ext.dom.Shadow',
            'Ext.dom.Shim',
            'Ext.dom.ElementEvent',
            'Ext.event.publisher.Dom',
            'Ext.event.publisher.Gesture',
            'Ext.event.publisher.ElementSize',
            'Ext.event.publisher.ElementPaint'
        ],
 
        uses: [
            'Ext.dom.Helper',
            'Ext.dom.CompositeElement',
            'Ext.dom.Fly',
            'Ext.dom.TouchAction',
            'Ext.event.publisher.Focus'
        ],
 
        observableType: 'element',
 
        isElement: true,
 
        skipGarbageCollection: true,
 
        $applyConfigs: true,
 
        identifiablePrefix: 'ext-element-',
 
        _selectDir: selectDir,
 
        styleHooks: {
            transform: {
                set: function(dom, value, el) {
                    var prop, result = '';
 
                    if (typeof value !== 'string') {
                        for (prop in value) {
                            if (result) {
                                result += ' ';
                            }
                            result += prop + '(' + value[prop] + ')';
                        }
                        value = result;
                    }
 
                    dom.style.transform = value;
                }
            }
        },
 
        validIdRe: Ext.validIdRe,
 
        blockedEvents: Ext.supports.EmulatedMouseOver ? {
            // mobile safari emulates a mouseover event on clickable elements such as 
            // anchors. This event is useless because it runs after touchend. We block 
            // this event to prevent mouseover handlers from running after tap events. It 
            // is up to the individual component to determine if it has an analog for 
            // mouseover, and implement the appropriate event handlers. 
            mouseover: 1
        } : {},
 
        longpressEvents: {
            longpress: 1,
            taphold: 1
        },
 
        /**
         * @property {Ext.Component} component
         * A reference to the `Component` that owns this element. This is `null` if there
         * is no direct owner.
         */
 
        //  Mouse events 
        /**
         * @event click
         * Fires when a mouse click is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event contextmenu
         * Fires when a right click is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event dblclick
         * Fires when a mouse double click is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mousedown
         * Fires when a mousedown is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mouseup
         * Fires when a mouseup is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mouseover
         * Fires when a mouseover is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mousemove
         * Fires when a mousemove is detected with the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mouseout
         * Fires when a mouseout is detected with the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mouseenter
         * Fires when the mouse enters the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event mouseleave
         * Fires when the mouse leaves the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
 
        //  Keyboard events 
        /**
         * @event keypress
         * Fires when a keypress is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event keydown
         * Fires when a keydown is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event keyup
         * Fires when a keyup is detected within the element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
 
        //  HTML frame/object events 
        /**
         * @event load
         * Fires when the user agent finishes loading all content within the element. Only supported by window, frames,
         * objects and images.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event unload
         * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target
         * element or any of its content has been removed.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event abort
         * Fires when an object/image is stopped from loading before completely loaded.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event error
         * Fires when an object/image/frame cannot be loaded properly.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event painted
         * Fires whenever this Element actually becomes visible (painted) on the screen. This is useful when you need to
         * perform 'read' operations on the DOM element, i.e: calculating natural sizes and positioning.
         *
         * __Note:__ This event is not available to be used with event delegation. Instead `painted` only fires if you explicitly
         * add at least one listener to it, for performance reasons.
         *
         * @param {Ext.dom.Element} this The component instance.
         */
        /**
         * @event resize
         * Important note: For the best performance on mobile devices, use this only when you absolutely need to monitor
         * a Element's size.
         *
         * __Note:__ This event is not available to be used with event delegation. Instead `resize` only fires if you explicitly
         * add at least one listener to it, for performance reasons.
         *
         * @param {Ext.dom.Element} this The component instance.
         * @param {Object} info The element's new size parameters.
         */
        /**
         * @event scroll
         * Fires when a document view is scrolled.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
 
        //  Form events 
        /**
         * @event select
         * Fires when a user selects some text in a text field, including input and textarea.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event change
         * Fires when a control loses the input focus and its value has been modified since gaining focus.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event submit
         * Fires when a form is submitted.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event reset
         * Fires when a form is reset.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event focus
         * Fires when an element receives focus either via the pointing device or by tab navigation.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event blur
         * Fires when an element loses focus either via the pointing device or by tabbing navigation.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event focusmove
         * Fires when focus is moved *within* an element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {Ext.dom.Element} e.target The {@link Ext.dom.Element} element which *recieved* focus.
         * @param {Ext.dom.Element} e.relatedTarget The {@link Ext.dom.Element} element which *lost* focus.
         * @param {HTMLElement} t The target of the event.
         */
 
        //  User Interface events 
        /**
         * @event DOMFocusIn
         * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMFocusOut
         * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMActivate
         * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
 
        //  DOM Mutation events 
        /**
         * @event DOMSubtreeModified
         * Where supported. Fires when the subtree is modified.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMNodeInserted
         * Where supported. Fires when a node has been added as a child of another node.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMNodeRemoved
         * Where supported. Fires when a descendant node of the element is removed.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMNodeRemovedFromDocument
         * Where supported. Fires when a node is being removed from a document.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMNodeInsertedIntoDocument
         * Where supported. Fires when a node is being inserted into a document.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMAttrModified
         * Where supported. Fires when an attribute has been modified.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
        /**
         * @event DOMCharacterDataModified
         * Where supported. Fires when the character data has been modified.
         * @param {Ext.event.Event} e The {@link Ext.event.Event} encapsulating the DOM event.
         * @param {HTMLElement} t The target of the event.
         */
 
        /**
         * Creates new Element directly by passing an id or the HTMLElement.  This
         * constructor should not be called directly.  Always use {@link Ext#get Ext.get()}
         * or {@link Ext#fly Ext#fly()} instead.
         *
         * In older versions of Ext JS and Sencha Touch this constructor checked to see if
         * there was already an instance of this element in the cache and if so, returned
         * the same instance. As of version 5 this behavior has been removed in order to
         * avoid a redundant cache lookup since the most common path is for the Element
         * constructor to be called from {@link Ext#get Ext.get()}, which has already
         * checked for a cache entry.
         *
         * Correct way of creating a new Ext.dom.Element (or retrieving it from the cache):
         *
         *     var el = Ext.get('foo'); // by id
         *
         *     var el = Ext.get(document.getElementById('foo')); // by DOM reference
         *
         * Incorrect way of creating a new Ext.dom.Element
         *
         *     var el = new Ext.dom.Element('foo');
         *
         * For quick and easy access to Ext.dom.Element methods use a flyweight:
         *
         *     Ext.fly('foo').addCls('foo-hovered');
         *
         * This simply attaches the DOM node with id='foo' to the global flyweight Element
         * instance to avoid allocating an extra Ext.dom.Element instance.  If, however,
         * the Element instance has already been cached by a previous call to Ext.get(),
         * then Ext.fly() will return the cached Element instance.  For more info see
         * {@link Ext#fly}.
         *
         * @param {String/HTMLElement} dom
         * @private
         */
        constructor: function(dom) {
            var me = this,
                id;
 
            if (typeof dom === 'string') {
                dom = DOC.getElementById(dom);
            }
 
            if (!dom) {
                //<debug> 
                Ext.raise("Invalid domNode reference or an id of an existing domNode: " + dom);
                //</debug> 
                return null;
            }
            //<debug> 
            if (Ext.cache[dom.id]) {
                Ext.raise("Element cache already contains an entry for id '" +
                    dom.id + "'.  Use Ext.get() to create or retrieve Element instances.");
            }
            //</debug> 
 
            /**
             * The DOM element
             * @property dom
             * @type HTMLElement
             */
            me.dom = dom;
 
            id = dom.id;
 
            if (id) {
                me.id = id;
            } else {
                id = dom.id = me.getUniqueId();
            }
 
            // Uncomment this when debugging orphaned Elements 
            // if (id === 'ext-element-5') debugger; 
 
            //<debug> 
            if (!me.validIdRe.test(me.id)) {
                Ext.raise('Invalid Element "id": "' + me.id + '"');
            }
            //</debug> 
 
            // set an "el" property that references "this".  This allows 
            // Ext.util.Positionable methods to operate on this.el.dom since it 
            // gets mixed into both Element and Component 
            me.el = me;
 
            Ext.cache[id] = me;
 
            me.longpressListenerCount = 0;
 
            me.mixins.observable.constructor.call(me);
        },
 
        inheritableStatics: {
            /**
             * @property
             * @private
             * @static
             * @inheritable
             */
            cache: Ext.cache = {},
 
            /**
             * @property
             * @static
             * @private
             * @inheritable
             */
            editableSelector: 'input,textarea,[contenteditable="true"]',
 
            /**
             * @property {Number}
             * Visibility mode constant for use with {@link Ext.dom.Element#setVisibilityMode}.
             * Use the CSS 'visibility' property to hide the element.
             *
             * Note that in this mode, {@link Ext.dom.Element#isVisible isVisible} may return true
             * for an element even though it actually has a parent element that is hidden. For this
             * reason, and in most cases, using the {@link #OFFSETS} mode is a better choice.
             * @static
             * @inheritable
             */
            VISIBILITY: 1,
 
            /**
             * @property {Number}
             * Visibility mode constant for use with {@link Ext.dom.Element#setVisibilityMode}.
             * Use the CSS 'display' property to hide the element.
             * @static
             * @inheritable
             */
            DISPLAY: 2,
 
            /**
             * @property {Number}
             * Visibility mode constant for use with {@link Ext.dom.Element#setVisibilityMode}.
             * Use CSS absolute positioning and top/left offsets to hide the element.
             * @static
             * @inheritable
             */
            OFFSETS: 3,
 
            /**
             * @property {Number}
             * Visibility mode constant for use with {@link Ext.dom.Element#setVisibilityMode}.
             * Use CSS `clip` property to reduce element's dimensions to 0px by 0px, effectively
             * making it hidden while not being truly invisible. This is useful when an element
             * needs to be published to the Assistive Technologies such as screen readers.
             * @static
             * @inheritable
             */
            CLIP: 4,
 
            /**
             * @property {Number}
             * Visibility mode constant for use with {@link Ext.dom.Element#setVisibilityMode}.
             * Use CSS `opacity` property to reduce element's opacity to 0
             * @static
             * @inheritable
             */
            OPACITY: 5,
 
            /**
             * @property
             * @static
             * @inheritable
             * @private
             * This property indicates a minimum threshold of vertical resize movement for
             * virtual keyboard detection.
             *
             * On some mobile browsers the framework needs to keep track of whether window
             * resize events were triggered by the opening or closing of a virtual keyboard
             * so that it can prevent unnecessary re-layout of the viewport.  It does this
             * by detecting resize events in the horizontal direction that occur immediately
             * after an editable element is focused or blurred.
             */
            minKeyboardHeight: 100,
 
            unitRe: unitRe,
 
            /**
             * @property {Boolean}
             * @private
             * @static
             * @inheritable
             * True to globally disable the delegated event system.  The results of
             * setting this to false are unpredictable since the Gesture publisher relies
             * on delegated events in order to work correctly.  Disabling delegated events
             * may cause Gestures to function incorrectly or to stop working completely.
             * Use at your own risk!
             */
            useDelegatedEvents: true,
 
            /**
             * @property {Object} validNodeTypes 
             * @private
             * @static
             * @inheritable
             * The list of valid nodeTypes that are allowed to be wrapped
             */
            validNodeTypes: {
                1: 1, // ELEMENT_NODE 
                9: 1 // DOCUMENT_NODE 
            },
 
            selectableCls: Ext.baseCSSPrefix + 'selectable',
            unselectableCls: Ext.baseCSSPrefix + 'unselectable',
 
            /**
             * Determines the maximum size for all ripples
             */
            maxRippleDiameter: 75,
 
            /**
             * Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
             * @param {Object} size The size to set.
             * @param {String} units The units to append to a numeric size value.
             * @return {String}
             * @private
             * @static
             * @inheritable
             */
            addUnits: function(size, units) {
                // Most common case first: Size is set to a number 
                if (typeof size === 'number') {
                    return size + (units || defaultUnit);
                }
 
                // Values which mean "auto" 
                // - "" 
                // - "auto" 
                // - undefined 
                // - null 
                if (size === "" || size === "auto" || size == null) {
                    return size || '';
                }
 
                // less common use case: number formatted as a string.  save this case until 
                // last to avoid regex execution if possible. 
                if (numberRe.test(size)) {
                    return size + (units || defaultUnit);
                }
 
                // Warn if it's not a valid CSS measurement 
                if (!unitRe.test(size)) {
                    //<debug> 
                    // Don't warn about calc() expressions 
                    if (!(Ext.isString(size) && size.indexOf('calc') === 0)) {
                        Ext.Logger.warn("Warning, size detected (" + size + ") not a valid property value on Element.addUnits.");
                    }
                    //</debug> 
                    return size || '';
                }
 
                return size;
            },
 
            /**
             * @private
             * Create method to add support for a DomHelper config. Creates
             * and appends elements/children using document.createElement/appendChild.
             * This method is used by the modern toolkit for a significant performance gain
             * in webkit browsers as opposed to using DomQuery which generates HTML
             * markup and sets it as innerHTML.
             *
             * However, the createElement/appendChild
             * method of creating elements is significantly slower in all versions of IE
             * at the time of this writing (6 - 11), so classic toolkit should not use this method,
             * but should instead use DomHelper methods, or Element methods that use
             * DomHelper under the hood (e.g. createChild).
             * see https:*fiddle.sencha.com/#fiddle/tj
             *
             * @static
             * @inheritable
             */
            create: function(attributes, domNode) {
                var me = this,
                    classes, element, elementStyle, tag, value, name, i, ln, tmp;
 
                attributes = attributes || {};
                if (attributes.isElement) {
                    return domNode ? attributes.dom : attributes;
                } else if ('nodeType' in attributes) {
                    return domNode ? attributes : Ext.get(attributes);
                }
 
                if (typeof attributes === 'string') {
                    return DOC.createTextNode(attributes);
                }
 
                tag = attributes.tag;
 
                if (!tag) {
                    tag = 'div';
                }
 
                if (attributes.namespace) {
                    element = DOC.createElementNS(attributes.namespace, tag);
                } else {
                    element = DOC.createElement(tag);
                }
 
                elementStyle = element.style;
 
                for (name in attributes) {
                    if (name !== 'tag') {
                        value = attributes[name];
 
                        switch (name) {
                            case 'style':
                                if (typeof value === 'string') {
                                    element.setAttribute(name, value);
                                }
                                else {
                                    for (in value) {
                                        if (value.hasOwnProperty(i)) {
                                            elementStyle[i] = value[i];
                                        }
                                    }
                                }
                                break;
 
                            case 'className':
                            case 'cls':
                                tmp = value.split(spacesRe);
                                classes = classes ? classes.concat(tmp) : tmp;
                                break;
 
                            case 'classList':
                                classes = classes ? classes.concat(value) : value;
                                break;
 
                            case 'text':
                                element.textContent = value;
                                break;
 
                            case 'html':
                                element.innerHTML = value;
                                break;
 
                            case 'hidden':
                                if (classes) {
                                    classes.push(displayCls);
                                } else {
                                    classes = [displayCls];
                                }
                                break;
 
                            case 'children':
                                if (value != null) {
                                    for (= 0,ln = value.length; i < ln; i++) {
                                        element.appendChild(me.create(value[i], true));
                                    }
                                }
                                break;
 
                            default:
                                if (value != null) { // skip null or undefined values 
                                    element.setAttribute(name, value);
                                }
                        }
                    }
                }
 
                if (classes) {
                    element.className = classes.join(' ');
                }
 
                if (domNode) {
                    return element;
                } else {
                    return me.get(element);
                }
            },
 
            /**
             * @inheritdoc Ext#fly
             * @inheritable
             * @static
             */
            fly: function(dom, named) {
                return Ext.fly(dom, named);
            },
 
            /**
             * Returns the top Element that is located at the passed coordinates in the current viewport.
             * @param {Number} x The x coordinate
             * @param {Number} y The y coordinate
             * @param {Boolean} [asDom=false] `true` to return a DOM element.
             * @return {Ext.dom.Element/HTMLElement} The found element.
             * @static
             * @inheritable
             * @method
             */
            fromPoint: (function() {
                // IE has a weird bug where elementFromPoint can fail on the first call when inside an iframe. 
                // It seems to happen more consistently on older IE, but sometimes crops up even in IE11. 
                // This plays havoc especially while running tests. 
                var elementFromPointBug;
                if (Ext.isIE || Ext.isEdge) {
                    try {
                        elementFromPointBug = window.self !== window.top;
                    } catch (e) {
                        elementFromPointBug = true;
                    }
                }
 
                return function(x, y, asDom) {
                    var el = null;
                    el = DOC.elementFromPoint(x, y);
                    if (!el && elementFromPointBug) {
                        el = DOC.elementFromPoint(x, y);
                    }
                    return asDom ? el : Ext.get(el);
                };
            })(),
 
            /**
             * Returns the top Element that is located at the passed coordinates taking into account
             * the scroll position of the document.
             * @static
             * @inheritable
             * @param {Number} x The x coordinate
             * @param {Number} y The y coordinate
             * @param {Boolean} [asDom=false] `true` to return a DOM element.
             * @return {Ext.dom.Element/HTMLElement} The found element.
             *
             * @since 6.2.0
             */
            fromPagePoint: function(x, y, asDom) {
                var scroll = Ext.getDoc().getScroll();
                return Element.fromPoint(- scroll.left, y - scroll.top, asDom);
            },
 
            /**
             * Retrieves Ext.dom.Element objects. {@link Ext#get} is alias for {@link Ext.dom.Element#get}.
             *
             * **This method does not retrieve {@link Ext.Component Component}s.** This method retrieves Ext.dom.Element
             * objects which encapsulate DOM elements. To retrieve a Component by its ID, use {@link Ext.ComponentManager#get}.
             *
             * When passing an id, it should not include the `#` character that is used for a css selector.
             *
             *     // For an element with id 'foo'
             *     Ext.get('foo'); // Correct
             *     Ext.get('#foo'); // Incorrect
             *
             * Uses simple caching to consistently return the same object. Automatically fixes if an object was recreated with
             * the same id via AJAX or DOM.
             *
             * @param {String/HTMLElement/Ext.dom.Element} el The `id` of the node, a DOM Node or an existing Element.
             * @return {Ext.dom.Element} The Element object (or `null` if no matching element was found).
             * @static
             * @inheritable
             */
            get: function(el) {
                var me = this,
                    cache = Ext.cache,
                    nodeType, dom, id, entry, isDoc, isWin, isValidNodeType;
 
                if (!el) {
                    return null;
                }
 
                //<debug> 
                function warnDuplicate(id) {
                    Ext.raise("DOM element with id " + id +
                        " in Element cache is not the same as element in the DOM. " +
                        "Make sure to clean up Element instances using destroy()" );
                }
                //</debug> 
 
                // Ext.get(flyweight) must return an Element instance, not the flyweight 
                if (el.isFly) {
                    el = el.dom;
                }
 
                if (typeof el === 'string') {
                    id = el;
                    if (cache.hasOwnProperty(id)) {
                        entry = cache[id];
                        if (entry.skipGarbageCollection || !Ext.isGarbage(entry.dom)) {
                            //<debug> 
                            dom = Ext.getElementById ? Ext.getElementById(id) :
                                DOC.getElementById(id);
                            if (dom && (dom !== entry.dom)) {
                                warnDuplicate(id);
                            }
                            //</debug> 
                            return entry;
                        } else {
                            entry.destroy();
                        }
                    }
 
                    if (id === windowId) {
                        return Element.get(WIN);
                    } else if (id === documentId) {
                        return Element.get(DOC);
                    }
 
                    // using Ext.getElementById() allows us to check the detached 
                    // body in addition to the body (Ext JS only). 
                    dom = Ext.getElementById ? Ext.getElementById(id) :
                        DOC.getElementById(id);
                    if (dom) {
                        return new Element(dom);
                    }
                }
 
                nodeType = el.nodeType;
 
                if (nodeType) {
                    isDoc = (nodeType === 9);
                    isValidNodeType = me.validNodeTypes[nodeType];
                } else {
                    // if an object has a window property that refers to itself we can 
                    // reasonably assume that it is a window object. 
                    // have to use == instead of === for IE8 
                    isWin = (el.window == el);
                }
 
                // check if we have a valid node type or if the el is a window object before 
                // proceeding. This allows elements, document fragments, and document/window 
                // objects (even those inside iframes) to be wrapped. 
                if (isValidNodeType || isWin) {
                    id = el.id;
 
                    if (cache.hasOwnProperty(id)) {
                        entry = cache[id];
                        if (entry.skipGarbageCollection || el === entry.dom ||
                            !Ext.isGarbage(entry.dom)) {
                            //<debug> 
                            if (el !== entry.dom) {
                                warnDuplicate(id);
                            }
                            //</debug> 
                            return entry;
                        } else {
                            entry.destroy();
                        }
                    }
 
                    if (el === DOC) {
                        el.id = documentId;
                    }
 
                    // Must use == here, otherwise IE fails to recognize the window 
                    if (el == WIN) {
                        el.id = windowId;
                    }
 
                    el = new Element(el);
 
                    if (isWin || isDoc) {
                        // document and window objects can never be garbage 
                        el.skipGarbageCollection = true;
                    }
                    return el;
                }
 
                if (el.isElement) {
                    return el;
                }
 
                if (el.isComposite) {
                    return el;
                }
 
                // Test for iterable. 
                // Allow the resulting Composite to be based upon an Array or HtmlCollection of nodes. 
                if (Ext.isIterable(el)) {
                    return me.select(el);
                }
 
                return null;
            },
 
            /**
             * Returns the active element in the DOM. If the browser supports activeElement
             * on the document, this is returned. If not, the focus is tracked and the active
             * element is maintained internally.
             * @static
             * @inheritable
             *
             * @param {Boolean} asElement Return Ext.Element instance instead of DOM node.
             *
             * @return {HTMLElement} The active (focused) element in the document.
             */
            getActiveElement: function(asElement) {
                var active = DOC.activeElement;
 
                // The activeElement can be null, however there also appears to be a very odd 
                // and inconsistent bug in IE where the activeElement is simply an empty object 
                // literal. Test if the returned active element has focus, if not, we've hit the bug 
                // so just default back to the document body. 
                if (!active || !active.focus) {
                    active = DOC.body;
                }
 
                return asElement ? Ext.get(active) : active;
            },
 
            /**
             * Retrieves the document height
             * @static
             * @inheritable
             * @return {Number} documentHeight 
             */
            getDocumentHeight: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollHeight : docEl.scrollHeight, this.getViewportHeight());
            },
 
            /**
             * Retrieves the document width
             * @static
             * @inheritable
             * @return {Number} documentWidth 
             */
            getDocumentWidth: function() {
                return Math.max(!Ext.isStrict ? DOC.body.scrollWidth : docEl.scrollWidth, this.getViewportWidth());
            },
 
            /**
             * Retrieves the current orientation of the window. This is calculated by
             * determining if the height is greater than the width.
             * @static
             * @inheritable
             * @return {String} Orientation of window: 'portrait' or 'landscape'
             */
            getOrientation: function() {
                if (Ext.supports.OrientationChange) {
                    return (WIN.orientation == 0) ? 'portrait' : 'landscape';
                }
 
                return (WIN.innerHeight > WIN.innerWidth) ? 'portrait' : 'landscape';
            },
 
            /**
             * Retrieves the viewport height of the window.
             * @static
             * @inheritable
             * @return {Number} viewportHeight 
             */
            getViewportHeight: function() {
                var viewportHeight = Element._viewportHeight;
 
                //<feature legacyBrowser> 
                if (Ext.isIE9m) {
                    return DOC.documentElement.clientHeight;
                }
                //</feature> 
 
                return (viewportHeight != null) ? viewportHeight : docEl.clientHeight;
            },
 
            /**
             * Retrieves the viewport width of the window.
             * @static
             * @inheritable
             * @return {Number} viewportWidth 
             */
            getViewportWidth: function() {
                var viewportWidth = Element._viewportWidth;
 
                //<feature legacyBrowser> 
                if (Ext.isIE9m) {
                    return DOC.documentElement.clientWidth;
                }
                //</feature> 
 
                return (viewportWidth != null) ? viewportWidth : docEl.clientWidth;
            },
 
            /**
             * Returns the current zoom level of the viewport as a ratio of page pixels to
             * screen pixels.
             * @private
             * @static
             * @return {Number}
             */
            getViewportScale: function() {
                // on desktop devices, the devicePixel ratio gives us the level of zoom that 
                // the user specified using ctrl +/- and or by selecting a zoom level from 
                // the menu. 
                // On android/iOS devicePixel ratio is a fixed number that represents the 
                // screen pixel density (e.g. always "2" on apple retina devices) 
 
                // WIN_TOP is guarded against cross-frame access in the closure above 
                var top = WIN_TOP;
 
                return ((Ext.isiOS || Ext.isAndroid) ? 1 :
                    (top.devicePixelRatio || // modern browsers 
                        top.screen.deviceXDPI / top.screen.logicalXDPI)) // IE10m 
                        * this.getViewportTouchScale();
            },
 
            /**
             * On touch-screen devices there may be an additional level of zooming
             * that occurs when the user performs a pinch or double-tap to zoom
             * gesture.  This is separate from and in addition to the
             * devicePixelRatio.  We can detect it by comparing the width
             * of the documentElement to window.innerWidth
             * @private
             */
            getViewportTouchScale: function(forceRead) {
                var scale = 1,
                    hidden = 'hidden',
                    // WIN_TOP is guarded against cross-frame access in the closure above 
                    top = WIN_TOP,
                    cachedScale;
 
                if (!forceRead) {
                    cachedScale = this._viewportTouchScale;
 
                    if (cachedScale) {
                        return cachedScale;
                    }
                }
 
                if (Ext.isIE10p || Ext.isEdge || Ext.isiOS) {
                    scale = docEl.offsetWidth / WIN.innerWidth;
                } else if (Ext.isChromeMobile) {
                    scale = top.outerWidth / top.innerWidth;
                }
 
                return scale;
            },
 
            /**
             * Retrieves the viewport size of the window.
             * @static
             * @inheritable
             * @return {Object} object containing width and height properties
             */
            getViewSize: function() {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            },
 
            /**
             * Checks if the passed size has a css unit attached.
             * @param {String} size The size.
             * @return {Boolean} `true` if the size has a css unit.
             *
             * @since 6.2.1
             */
            hasUnit: function(size) {
                return !!(size && unitRe.test(size));
            },
 
 
            /**
             * Checks if the passed css unit is a relative unit. This includes:
             * - `auto`
             * - `%`
             * - `em`
             * - `rem`
             * - `auto`
             * - `vh`
             * - `vw`
             * - `vmin`
             * - `vmax`
             * - `ex
             * - `ch`
             * @param {String} The css unit and value.
             * @return {Boolean} `true` if the value is relative.
             *
             * @since 6.2.0
             */
            isRelativeUnit: function(size) {
                return !size || relativeUnitRe.test(size);
            },
 
            /**
             * Mask iframes when shim is true. See {@link Ext.util.Floating#shim}.
             * @private
             */
            maskIframes: function() {
                var iframes = document.getElementsByTagName('iframe'),
                    fly = new Ext.dom.Fly();
 
                Ext.each(iframes, function(iframe) {
                    var myMask;
 
                    myMask = fly.attach(iframe.parentNode).mask();
                    myMask.setStyle('background-color','transparent');
                });
            },
 
            /**
             * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
             * For example:
             *
             * - border-width -> borderWidth
             * - padding-top -> paddingTop
             *
             * @static
             * @inheritable
             * @param {String} prop The property to normalize
             * @return {String} The normalized string
             */
            normalize: function(prop) {
                return propertyCache[prop] || (propertyCache[prop] = prop.replace(camelRe, camelReplaceFn));
            },
 
 
            /**
             * @private
             * @static
             * @inheritable
             */
            _onWindowFocusChange: function(e) {
                // Tracks the timestamp of focus entering or leaving an editable element 
                // so that we can compare this timestamp to the time of the next window 
                // resize for the purpose of determining if the virtual keyboard is displayed 
                // see _onWindowResize for more details 
                if (Ext.fly(e.target).is(Element.editableSelector)) {
                    lastFocusChange = new Date();
                    editableHasFocus = (e.type === 'focusin' || e.type === 'pointerup');
                }
            },
 
            /**
             * @private
             * @static
             * @inheritable
             */
            _onWindowResize: function() {
                var documentWidth = docEl.clientWidth,
                    documentHeight = docEl.clientHeight,
                    now = new Date(),
                    threshold = 1000,
                    deltaX, deltaY;
 
                deltaX = documentWidth - Element._documentWidth;
                deltaY = documentHeight - Element._documentHeight;
 
                Element._windowWidth = documentWidth;
                Element._windowHeight = documentHeight;
 
                // If the focus entered or left an editable element within a brief threshold 
                // of time, then this resize event MAY be due to a virtual keyboard being 
                // shown or hidden.  Let's do some additional checking to find out. 
                if (((now - lastFocusChange) < threshold) || ((now - lastKeyboardClose) < threshold)) {
                    // If the resize is ONLY in the vertical direction, and an editable 
                    // element has the focus, and the vertical movement was significant, 
                    // we can be reasonably certain that the resize event was due to 
                    // a virtual keyboard being opened. 
                    if (deltaX === 0 && (editableHasFocus && (deltaY <= -Element.minKeyboardHeight))) {
                        isVirtualKeyboardOpen = true;
                        return;
                    }
                }
 
                if (isVirtualKeyboardOpen && (deltaX === 0) && (deltaY >= Element.minKeyboardHeight)) {
                    isVirtualKeyboardOpen = false;
                    // when windows tablets are rotated while keyboard is open, the keyboard closes 
                    // and then immediately reopens.  Track the timestamp of the last keyboard 
                    // close so that we can detect a successive resize event that might indicate 
                    // reopening 
                    lastKeyboardClose = new Date();
                }
 
                if (isVirtualKeyboardOpen) {
                    return;
                }
 
                // These cached variables are used by getViewportWidth and getViewportHeight 
                // They do not get updated if we returned early due to detecting  that the 
                // resize event was triggered by virtual keyboard. 
                Element._viewportWidth = documentWidth;
                Element._viewportHeight = documentHeight;
            },
 
            /**
             * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
             * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
             * @static
             * @inheritable
             * @param {Number/String} box The encoded margins
             * @return {Object} An object with margin sizes for top, right, bottom and left containing the unit
             */
            parseBox: function(box) {
                box = box || 0;
 
                var type = typeof box,
                    parts,
                    ln;
 
                if (type === 'number') {
                    return {
                        top: box,
                        right: box,
                        bottom: box,
                        left: box
                    };
                } else if (type !== 'string') {
                    // If not a number or a string, assume we've been given a box config. 
                    return box;
                }
 
                parts  = box.split(' ');
                ln = parts.length;
 
                if (ln === 1) {
                    parts[1] = parts[2] = parts[3] = parts[0];
                } else if (ln === 2) {
                    parts[2] = parts[0];
                    parts[3] = parts[1];
                } else if (ln === 3) {
                    parts[3] = parts[1];
                }
 
                return {
                    top: parseFloat(parts[0]) || 0,
                    right: parseFloat(parts[1]) || 0,
                    bottom: parseFloat(parts[2]) || 0,
                    left: parseFloat(parts[3]) || 0
                };
            },
 
            /**
             * Converts a CSS string into an object with a property for each style.
             *
             * The sample code below would return an object with 2 properties, one
             * for background-color and one for color.
             *
             *     var css = 'background-color: red; color: blue;';
             *     console.log(Ext.dom.Element.parseStyles(css));
             *
             * @static
             * @inheritable
             * @param {String} styles A CSS string
             * @return {Object} styles 
             */
            parseStyles: function(styles) {
                var out = {},
                    matches;
 
                if (styles) {
                    // Since we're using the g flag on the regex, we need to set the lastIndex. 
                    // This automatically happens on some implementations, but not others, see: 
                    // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls 
                    // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp 
                    cssRe.lastIndex = 0;
                    while ((matches = cssRe.exec(styles))) {
                        out[matches[1]] = matches[2] || '';
                    }
                }
                return out;
            },
 
            /**
             * Selects elements based on the passed CSS selector to enable
             * {@link Ext.dom.Element Element} methods to be applied to many related
             * elements in one statement through the returned
             * {@link Ext.dom.CompositeElementLite CompositeElementLite} object.
             * @static
             * @inheritable
             * @param {String/HTMLElement[]} selector The CSS selector or an array of
             * elements
             * @param {Boolean} [composite=false] Return a CompositeElement as opposed to
             * a CompositeElementLite. Defaults to false.
             * @param {HTMLElement/String} [root] The root element of the query or id of
             * the root
             * @return {Ext.dom.CompositeElementLite/Ext.dom.CompositeElement}
             */
            select: function(selector, composite, root) {
                return Ext.fly(root || DOC).select(selector, composite);
            },
 
            /**
             * Selects child nodes of a given root based on the passed CSS selector.
             * @static
             * @inheritable
             * @param {String} selector The CSS selector.
             * @param {Boolean} [asDom=true] `false` to return an array of Ext.dom.Element
             * @param {HTMLElement/String} [root] The root element of the query or id of
             * the root
             * @return {HTMLElement[]/Ext.dom.Element[]} An Array of elements that match
             * the selector.  If there are no matches, an empty Array is returned.
             */
            query: function(selector, asDom, root) {
                return Ext.fly(root || DOC).query(selector, asDom);
            },
 
            /**
             * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
             * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
             * @static
             * @inheritable
             * @param {Number/String/Object} box The encoded margins, or an object with top, right,
             * @param {String} units The type of units to add
             * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
             */
            unitizeBox: function(box, units) {
                var me = this;
                box = me.parseBox(box);
 
                return me.addUnits(box.top, units) + ' ' +
                    me.addUnits(box.right, units) + ' ' +
                    me.addUnits(box.bottom, units) + ' ' +
                    me.addUnits(box.left, units);
            },
 
            /**
             * Unmask iframes when shim is true. See {@link Ext.util.Floating#cfg-shim}.
             * @private
             */
            unmaskIframes: function() {
                var iframes = document.getElementsByTagName('iframe'),
                    fly = new Ext.dom.Fly();
 
                Ext.each(iframes, function(iframe) {
                    fly.attach(iframe.parentNode).unmask();
                });
            },
 
            /**
             * Serializes a DOM form into a url encoded string
             * @param {Object} form The form
             * @return {String} The url encoded form
             * @static
             * @inheritable
             */
            serializeForm: function(form) {
                var fElements = form.elements || (DOC.forms[form] || Ext.getDom(form)).elements,
                    hasSubmit = false,
                    encoder = encodeURIComponent,
                    data = '',
                    eLen = fElements.length,
                    element, name, type, options, hasValue, e,
                    o, oLen, opt;
 
                for (= 0; e < eLen; e++) {
                    element = fElements[e];
                    name = element.name;
                    type = element.type;
                    options = element.options;
 
                    if (!element.disabled && name) {
                        if (/select-(one|multiple)/i.test(type)) {
                            oLen = options.length;
                            for (= 0; o < oLen; o++) {
                                opt = options[o];
                                if (opt.selected) {
                                    hasValue = opt.hasAttribute('value');
                                    data += Ext.String.format('{0}={1}&', encoder(name), encoder(hasValue ? opt.value : opt.text));
                                }
                            }
                        } else if (!(/file|undefined|reset|button/i.test(type))) {
                            if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
                                data += encoder(name) + '=' + encoder(element.value) + '&';
                                hasSubmit = /submit/i.test(type);
                            }
                        }
                    }
                }
                return data.substr(0, data.length - 1);
            },
 
            /**
             * Returns the common ancestor of the two passed elements.
             * @static
             * @inheritable
             *
             * @param {Ext.dom.Element/HTMLElement} nodeA
             * @param {Ext.dom.Element/HTMLElement} nodeB
             * @param {Boolean} returnDom Pass `true` to return a DOM element. Otherwise An {@link Ext.dom.Element Element} will be returned.
             * @return {Ext.dom.Element/HTMLElement} The common ancestor.
             */
            getCommonAncestor: function(nodeA, nodeB, returnDom) {
                caFly = caFly || new Ext.dom.Fly();
                caFly.attach(Ext.getDom(nodeA));
                while (!caFly.isAncestor(nodeB)) {
                    if (caFly.dom.parentNode) {
                        caFly.attach(caFly.dom.parentNode);
                    }
                    // If Any of the nodes in in a detached state, have to use the document.body 
                    else {
                        caFly.attach(DOC.body);
                        break;
                    }
                }
                return returnDom ? caFly.dom : Ext.get(caFly);
            }
        },
 
        /**
        * Enable text selection for this element (normalized across browsers)
        * @return {Ext.dom.Element} this
        */
        selectable: function() {
            var me = this;
 
            // We clear this property for all browsers, not just Opera. This is so that rendering templates don't need to 
            // condition on Opera when making elements unselectable. 
            me.dom.unselectable = '';
 
            me.removeCls(Element.unselectableCls);
            me.addCls(Element.selectableCls);
 
            return me;
        },
 
        /**
         * Disables text selection for this element (normalized across browsers)
         * @return {Ext.dom.Element} this
         */
        unselectable: function() {
            // The approach used to disable text selection combines CSS, HTML attributes and DOM events. Importantly the 
            // strategy is designed to be expressible in markup, so that elements can be rendered unselectable without 
            // needing modifications post-render. e.g.: 
            // 
            // <div class="x-unselectable" unselectable="on"></div> 
            // 
            // Changes to this method may need to be reflected elsewhere, e.g. ProtoElement. 
            var me = this;
 
            // The unselectable property (or similar) is supported by various browsers but Opera is the only browser that 
            // doesn't support any of the other techniques. The problem with it is that it isn't inherited by child 
            // elements. Theoretically we could add it to all children but the performance would be terrible. In certain 
            // key locations (e.g. panel headers) we add unselectable="on" to extra elements during rendering just for 
            // Opera's benefit. 
            if (Ext.isOpera) {
                me.dom.unselectable = 'on';
            }
 
            // In Mozilla and WebKit the CSS properties -moz-user-select and -webkit-user-select prevent a selection 
            // originating in an element. These are inherited, which is what we want. 
            // 
            // In IE we rely on a listener for the selectstart event instead. We don't need to register a listener on the 
            // individual element, instead we use a single listener and rely on event propagation to listen for the event at 
            // the document level. That listener will walk up the DOM looking for nodes that have either of the classes 
            // x-selectable or x-unselectable. This simulates the CSS inheritance approach. 
            // 
            // IE 10 is expected to support -ms-user-select so the listener may not be required. 
            me.removeCls(Element.selectableCls);
            me.addCls(Element.unselectableCls);
 
            return me;
        },
 
        // statics 
        statics: {
            // This selector will be modified at runtime in the _init() method above 
            // to include the elements with saved tabindex in the returned set 
            tabbableSelector: Ext.supports.CSS3NegationSelector
                ? 'a[href],button,iframe,input,select,textarea,[tabindex]:not([tabindex="-1"]),[contenteditable="true"]'
                : 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]',
 
            // Anchor and link tags are special; they are only naturally focusable (and tabbable) 
            // if they have href attribute, and tabbabledness is further platform/browser specific. 
            // Thus we check it separately in the code. 
            naturallyFocusableTags: {
                BUTTON: true,
                IFRAME: true,
                EMBED: true,
                INPUT: true,
                OBJECT: true,
                SELECT: true,
                TEXTAREA: true,
                HTML: Ext.isIE ? true : false,
                BODY: Ext.isIE ? false: true
            },
 
            // <object> element is naturally tabbable only in IE8 and below 
            naturallyTabbableTags: {
                BUTTON: true,
                IFRAME: true,
                INPUT: true,
                SELECT: true,
                TEXTAREA: true,
                OBJECT: Ext.isIE8m ? true : false
            },
 
            inputTags: {
                INPUT: true,
                TEXTAREA: true
            },
 
            tabbableSavedCounterAttribute: 'data-tabindex-counter',
            tabbableSavedValueAttribute: 'data-tabindex-value',
 
            splitCls: function (cls) {
                if (typeof cls === 'string') {
                    cls = cls.split(spacesRe);
                }
                return cls;
            }
        }, // statics 
 
        _init: function(E) {
            // Allow overriding the attribute name and/or selector; this is 
            // done only once for performance reasons 
            E.tabbableSelector += ',[' + E.tabbableSavedCounterAttribute + ']';
        },
 
        /**
         * Adds the given CSS class(es) to this Element.
         * @param {String/String[]} names The CSS classes to add separated by space,
         * or an array of classes
         * @param {String} [prefix] Prefix to prepend to each class. The separator `-` will be
         * appended to the prefix.
         * @param {String} [suffix] Suffix to append to each class. The separator `-` will be
         * prepended to the suffix.
         * @return {Ext.dom.Element} this
         */
        addCls: function (names, prefix, suffix) {
            return this.replaceCls(null, names, prefix, suffix);
        },
 
        /**
         * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
         * @param {String} className The class to add
         * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter
         * will be the Element instance. If this functions returns false, the class will not be added.
         * @param {Object} [scope] The scope to execute the testFn in.
         * @return {Ext.dom.Element} this
         */
        addClsOnClick: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
 
            me.on("mousedown", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                me.addCls(className);
                Ext.getDoc().on({
                    mouseup: function() {
                        // In case me was destroyed prior to mouseup 
                        if (me.dom) {
                            me.removeCls(className);
                        }
                    },
                    single: true
                });
            });
            return me;
        },
 
        /**
         * Sets up event handlers to add and remove a css class when this element has the focus
         * @param {String} className The class to add
         * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter
         * will be the Element instance. If this functions returns false, the class will not be added.
         * @param {Object} [scope] The scope to execute the testFn in.
         * @return {Ext.dom.Element} this
         */
        addClsOnFocus: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
 
            me.on("focus", function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return false;
                }
                me.addCls(className);
            });
            me.on("blur", function() {
                // In case blur is caused by destruction of me 
                if (me.dom) {
                    me.removeCls(className);
                }
            });
            return me;
        },
 
        /**
         * Sets up event handlers to add and remove a css class when the mouse is over this element
         * @param {String} className The class to add
         * @param {Function} [testFn] A test function to execute before adding the class. The passed parameter
         * will be the Element instance. If this functions returns false, the class will not be added.
         * @param {Object} [scope] The scope to execute the testFn in.
         * @return {Ext.dom.Element} this
         */
        addClsOnOver: function(className, testFn, scope) {
            var me = this,
                hasTest = Ext.isFunction(testFn);
 
            me.hover(
                function() {
                    if (hasTest && testFn.call(scope || me, me) === false) {
                        return;
                    }
                    me.addCls(className);
                },
                function() {
                    me.removeCls(className);
                }
            );
            return me;
        },
 
        addStyles: function(sides, styles){
            var totalSize = 0,
                sidesArr = (sides || '').match(wordsRe),
                i,
                len = sidesArr.length,
                side,
                styleSides = [];
 
            if (len === 1) {
                totalSize = Math.abs(parseFloat(this.getStyle(styles[sidesArr[0]])) || 0);
            } else if (len) {
                for (= 0; i < len; i++) {
                    side = sidesArr[i];
                    styleSides.push(styles[side]);
                }
                //Gather all at once, returning a hash 
                styleSides = this.getStyle(styleSides);
 
                for (i=0; i < len; i++) {
                    side = sidesArr[i];
                    totalSize += parseFloat(styleSides[styles[side]]) || 0;
                }
            }
 
            return totalSize;
        },
 
        addUnits: function(size, units) {
            return Element.addUnits(size, units);
        },
 
        // The following 3 methods add just enough of an animation api to make the scroller work 
        // in Sencha Touch 
        // TODO: unify touch/ext animations 
        animate: function(animation) {
            animation = new Ext.fx.Animation(animation);
            animation.setElement(this);
            this._activeAnimation = animation;
            animation.on({
                animationend: this._onAnimationEnd,
                scope: this
            });
            Ext.Animator.run(animation);
            return animation;
        },
 
        _onAnimationEnd: function() {
            this._activeAnimation = null;
        },
 
        getActiveAnimation: function() {
            return this._activeAnimation;
        },
 
        append: function() {
            return this.appendChild.apply(this, arguments);
        },
 
        /**
         * Appends the passed element(s) to this element
         * @param {String/HTMLElement/Ext.dom.Element/Object} el The id or element to insert
         * or a DomHelper config
         * @param {Boolean} [returnDom=false] True to return the raw DOM element instead
         * of Ext.dom.Element
         * @return {Ext.dom.Element/HTMLElement} The inserted Ext.dom.Element (or
         * HTMLElement if _returnDom_ is _true_).
         */
        appendChild: function(el, returnDom) {
            var me = this,
                insertEl,
                eLen, e;
 
            if (el.nodeType || el.dom || typeof el === 'string') { // element 
                el = Ext.getDom(el);
                me.dom.appendChild(el);
                return !returnDom ? Ext.get(el) : el;
            } else if (el.length) {
                // append all elements to a documentFragment 
                insertEl = Ext.fly(DOC.createDocumentFragment());
                eLen = el.length;
 
                for (= 0; e < eLen; e++) {
                    insertEl.appendChild(el[e], returnDom);
                }
                el = Ext.Array.toArray(insertEl.dom.childNodes);
                me.dom.appendChild(insertEl.dom);
                return returnDom ? el : new Ext.dom.CompositeElementLite(el);
            }
            else { // dh config 
                return me.createChild(el, null, returnDom);
            }
        },
 
        /**
         * Appends this element to the passed element.
         * @param {String/HTMLElement/Ext.dom.Element} el The new parent element.
         * The id of the node, a DOM Node or an existing Element.
         * @return {Ext.dom.Element} This element.
         */
        appendTo: function(el) {
            Ext.getDom(el).appendChild(this.dom);
            return this;
        },
 
        /**
         * More flexible version of {@link #setStyle} for setting style properties.
         *
         * Styles in object form should be a valid DOM element style property.
         * [Valid style property names](http://www.w3schools.com/jsref/dom_obj_style.asp)
         * (_along with the supported CSS version for each_)
         *
         *     // <div id="my-el">Phineas Flynn</div>
         *
         *     var el = Ext.get('my-el');
         *
         *     el.applyStyles('color: white;');
         *
         *     el.applyStyles({
         *         fontWeight: 'bold',
         *         backgroundColor: 'gray',
         *         padding: '10px'
         *     });
         *
         *     el.applyStyles(function () {
         *         if (name.initialConfig.html === 'Phineas Flynn') {
         *             return 'font-style: italic;';
         *             // OR return { fontStyle: 'italic' };
         *         }
         *     });
         *
         * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form `{width:"100px"}`, or
         * a function which returns such a specification.
         * @return {Ext.dom.Element} this
         */
        applyStyles: function(styles) {
            if (styles) {
                if (typeof styles === "function") {
                    styles = styles.call();
                }
                if (typeof styles === "string") {
                    styles = Element.parseStyles(styles);
                }
                if (typeof styles === "object") {
                    this.setStyle(styles);
                }
            }
            return this;
        },
 
        /**
         * Tries to blur the element. Any exceptions are caught and ignored.
         * @return {Ext.dom.Element} this
         */
        blur: function() {
            var me = this,
                dom = me.dom;
            // In IE, blurring the body can cause the browser window to hide. 
            // Blurring the body is redundant, so instead we just focus it 
            if (dom !== DOC.body) {
                try {
                    dom.blur();
                } catch(e) {
                }
                return me;
            } else {
                return me.focus(undefined, dom);
            }
        },