/*
This file is part of Ext JS 4.2

Copyright (c) 2011-2013 Sencha Inc

Contact:  http://www.sencha.com/contact

GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.

Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.

If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.

Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314)
*/
// @tag dom,core
/**
 */
Ext.define('Ext.dom.Element_style', {
    override: 'Ext.dom.Element'
},
function() {

var Element = this,
    view = document.defaultView,
    adjustDirect2DTableRe = /table-row|table-.*-group/,
    INTERNAL = '_internal',
    HIDDEN = 'hidden',
    HEIGHT = 'height',
    WIDTH = 'width',
    ISCLIPPED = 'isClipped',
    OVERFLOW = 'overflow',
    OVERFLOWX = 'overflow-x',
    OVERFLOWY = 'overflow-y',
    ORIGINALCLIP = 'originalClip',
    DOCORBODYRE = /#document|body/i,
    // This reduces the lookup of 'me.styleHooks' by one hop in the prototype chain. It is
    // the same object.
    styleHooks, verticalStyleHooks90, verticalStyleHooks270,
    edges, k, edge, borderWidth;

if (!view || !view.getComputedStyle) {
    Element.prototype.getStyle = function (property, inline) {
        var me = this,
            dom = me.dom,
            multiple = typeof property != 'string',
            hooks = me.styleHooks,
            prop = property,
            props = prop,
            len = 1,
            isInline = inline,
            camel, domStyle, values, hook, out, style, i;

        if (multiple) {
            values = {};
            prop = props[0];
            i = 0;
            if (!(len = props.length)) {
                return values;
            }
        }

        if (!dom || dom.documentElement) {
            return values || '';
        }

        domStyle = dom.style;

        if (inline) {
            style = domStyle;
        } else {
            style = dom.currentStyle;

            // fallback to inline style if rendering context not available
            if (!style) {
                isInline = true;
                style = domStyle;
            }
        }

        do {
            hook = hooks[prop];

            if (!hook) {
                hooks[prop] = hook = { name: Element.normalize(prop) };
            }

            if (hook.get) {
                out = hook.get(dom, me, isInline, style);
            } else {
                camel = hook.name;

                // In some cases, IE6 will throw Invalid Argument exceptions for properties
                // like fontSize (/examples/tabs/tabs.html in 4.0 used to exhibit this but
                // no longer does due to font style changes). There is a real cost to a try
                // block, so we avoid it where possible...
                if (hook.canThrow) {
                    try {
                        out = style[camel];
                    } catch (e) {
                        out = '';
                    }
                } else {
                    // EXTJSIV-5657 - In IE9 quirks mode there is a chance that VML root element 
                    // has neither `currentStyle` nor `style`. Return '' this case.
                    out = style ? style[camel] : '';
                }
            }

            if (!multiple) {
                return out;
            }

            values[prop] = out;
            prop = props[++i];
        } while (i < len);

        return values;
    };
}

Element.override({
    getHeight: function(contentHeight, preciseHeight) {
        var me = this,
            hidden = me.isStyle('display', 'none'),
            height,
            floating;

        if (hidden) {
            return 0;
        }

        height = me.dom.offsetHeight;

        // IE9/10 Direct2D dimension rounding bug
        if (Ext.supports.Direct2DBug) {
            floating = me.adjustDirect2DDimension(HEIGHT);
            if (preciseHeight) {
                height += floating;
            }
            else if (floating > 0 && floating < 0.5) {
                height++;
            }
        }

        if (contentHeight) {
            height -= me.getBorderWidth("tb") + me.getPadding("tb");
        }

        return (height < 0) ? 0 : height;
    },

    getWidth: function(contentWidth, preciseWidth) {
        var me = this,
            dom = me.dom,
            hidden = me.isStyle('display', 'none'),
            rect, width, floating;

        if (hidden) {
            return 0;
        }

        // Gecko will in some cases report an offsetWidth that is actually less than the width of the
        // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
        // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
        // subpixel measurements so we can force them to always be rounded up. See
        // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
        // Rounding up ensures that the width includes the full width of the text contents.
        if (preciseWidth && Ext.supports.BoundingClientRect) {
            rect = dom.getBoundingClientRect();
            // IE9 is the only browser that supports getBoundingClientRect() and
            // uses a filter to rotate the element vertically.  When a filter
            // is used to rotate the element, the getHeight/getWidth functions
            // are not inverted (see setVertical).
            width = (me.vertical && !Ext.isIE9 && !Ext.supports.RotatedBoundingClientRect) ?
                    (rect.bottom - rect.top) : (rect.right - rect.left);
        } else {
            width = dom.offsetWidth;
        }

        // IE9/10 Direct2D dimension rounding bug: https://sencha.jira.com/browse/EXTJSIV-603
        // there is no need make adjustments for this bug when the element is vertically
        // rotated because the width of a vertical element is its rotated height
        if (Ext.supports.Direct2DBug && !me.vertical) {
            // get the fractional portion of the sub-pixel precision width of the element's text contents
            floating = me.adjustDirect2DDimension(WIDTH);
            if (preciseWidth) {
                width += floating;
            }
            // IE9 also measures fonts with sub-pixel precision, but unlike Gecko, instead of rounding the offsetWidth down,
            // it rounds to the nearest integer. This means that in order to ensure that the width includes the full
            // width of the text contents we need to increment the width by 1 only if the fractional portion is less than 0.5
            else if (floating > 0 && floating < 0.5) {
                width++;
            }
        }

        if (contentWidth) {
            width -= me.getBorderWidth("lr") + me.getPadding("lr");
        }

        return (width < 0) ? 0 : width;
    },

    setWidth: function(width, animate) {
        var me = this;
        width = me.adjustWidth(width);
        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width
                }
            }, animate));
        }
        return me;
    },

    setHeight : function(height, animate) {
        var me = this;

        height = me.adjustHeight(height);
        if (!animate || !me.anim) {
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    height: height
                }
            }, animate));
        }

        return me;
    },

    applyStyles: function(style) {
        Ext.DomHelper.applyStyles(this.dom, style);
        return this;
    },

    setSize: function(width, height, animate) {
        var me = this;

        if (Ext.isObject(width)) { // in case of object from getSize()
            animate = height;
            height = width.height;
            width = width.width;
        }

        width = me.adjustWidth(width);
        height = me.adjustHeight(height);

        if (!animate || !me.anim) {
            me.dom.style.width = me.addUnits(width);
            me.dom.style.height = me.addUnits(height);
        }
        else {
            if (animate === true) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    width: width,
                    height: height
                }
            }, animate));
        }

        return me;
    },

    getViewSize : function() {
        var me = this,
            dom = me.dom,
            isDoc = DOCORBODYRE.test(dom.nodeName),
            ret;

        // If the body, use static methods
        if (isDoc) {
            ret = {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        } else {
            ret = {
                width : dom.clientWidth,
                height : dom.clientHeight
            };
        }

        return ret;
    },

    getSize: function(contentSize) {
        return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
    },

    // TODO: Look at this

    // private  ==> used by Fx
    adjustWidth : function(width) {
        var me = this,
            isNum = (typeof width == 'number');

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
        }
        return (isNum && width < 0) ? 0 : width;
    },

    // private   ==> used by Fx
    adjustHeight : function(height) {
        var me = this,
            isNum = (typeof height == "number");

        if (isNum && me.autoBoxAdjust && !me.isBorderBox()) {
            height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
        }
        return (isNum && height < 0) ? 0 : height;
    },

    /**
     * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like `#fff`) and valid values
     * are convert to standard 6 digit hex color.
     * @param {String} attr The css attribute
     * @param {String} defaultValue The default value to use when a valid color isn't found
     * @param {String} [prefix] defaults to #. Use an empty string when working with
     * color anims.
     */
    getColor : function(attr, defaultValue, prefix) {
        var v = this.getStyle(attr),
            color = prefix || prefix === '' ? prefix : '#',
            h, len, i=0;

        if (!v || (/transparent|inherit/.test(v))) {
            return defaultValue;
        }
        if (/^r/.test(v)) {
             v = v.slice(4, v.length - 1).split(',');
             len = v.length;
             for (; i<len; i++) {
                h = parseInt(v[i], 10);
                color += (h < 16 ? '0' : '') + h.toString(16);
            }
        } else {
            v = v.replace('#', '');
            color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
        }
        return(color.length > 5 ? color.toLowerCase() : defaultValue);
    },

    /**
     * Set the opacity of the element
     * @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
     * @param {Boolean/Object} [animate] a standard Element animation config object or `true` for
     * the default animation (`{duration: 350, easing: 'easeIn'}`)
     * @return {Ext.dom.Element} this
     */
    setOpacity: function(opacity, animate) {
        var me = this;

        if (!me.dom) {
            return me;
        }

        if (!animate || !me.anim) {
            me.setStyle('opacity', opacity);
        }
        else {
            if (typeof animate != 'object') {
                animate = {
                    duration: 350,
                    easing: 'ease-in'
                };
            }

            me.animate(Ext.applyIf({
                to: {
                    opacity: opacity
                }
            }, animate));
        }
        return me;
    },

    /**
     * Clears any opacity settings from this element. Required in some cases for IE.
     * @return {Ext.dom.Element} this
     */
    clearOpacity : function() {
        return this.setOpacity('');
    },

    /**
     * @private
     * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
     * @return {Number} 0 or 1
     */
    adjustDirect2DDimension: function(dimension) {
        var me = this,
            dom = me.dom,
            display = me.getStyle('display'),
            inlineDisplay = dom.style.display,
            inlinePosition = dom.style.position,
            originIndex = dimension === WIDTH ? 0 : 1,
            currentStyle = dom.currentStyle,
            floating;

        if (display === 'inline') {
            dom.style.display = 'inline-block';
        }

        dom.style.position = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';

        // floating will contain digits that appears after the decimal point
        // if height or width are set to auto we fallback to msTransformOrigin calculation
        
        // Use currentStyle here instead of getStyle. In some difficult to reproduce 
        // instances it resets the scrollWidth of the element
        floating = (parseFloat(currentStyle[dimension]) || parseFloat(currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;

        dom.style.position = inlinePosition;

        if (display === 'inline') {
            dom.style.display = inlineDisplay;
        }

        return floating;
    },

    /**
     * Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
     * @return {Ext.dom.Element} this
     */
    clip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            style;

        if (!data[ISCLIPPED]) {
            data[ISCLIPPED] = true;
            style = me.getStyle([OVERFLOW, OVERFLOWX, OVERFLOWY]);
            data[ORIGINALCLIP] = {
                o: style[OVERFLOW],
                x: style[OVERFLOWX],
                y: style[OVERFLOWY]
            };
            me.setStyle(OVERFLOW, HIDDEN);
            me.setStyle(OVERFLOWX, HIDDEN);
            me.setStyle(OVERFLOWY, HIDDEN);
        }
        return me;
    },

    /**
     * Return clipping (overflow) to original clipping before {@link #clip} was called
     * @return {Ext.dom.Element} this
     */
    unclip : function() {
        var me = this,
            data = (me.$cache || me.getCache()).data,
            clip;

        if (data[ISCLIPPED]) {
            data[ISCLIPPED] = false;
            clip = data[ORIGINALCLIP];
            if (clip.o) {
                me.setStyle(OVERFLOW, clip.o);
            }
            if (clip.x) {
                me.setStyle(OVERFLOWX, clip.x);
            }
            if (clip.y) {
                me.setStyle(OVERFLOWY, clip.y);
            }
        }
        return me;
    },

    /**
     * Wraps the specified element with a special 9 element markup/CSS block that renders by default as
     * a gray container with a gradient background, rounded corners and a 4-way shadow.
     *
     * This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
     * {@link Ext.panel.Panel} when {@link Ext.panel.Panel#frame frame=true}, {@link Ext.window.Window}).
     * The markup is of this form:
     *
     *     Ext.dom.Element.boxMarkup =
     *     '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
     *     <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
     *     <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
     *
     * Example usage:
     *
     *     // Basic box wrap
     *     Ext.get("foo").boxWrap();
     *
     *     // You can also add a custom class and use CSS inheritance rules to customize the box look.
     *     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
     *     // for how to create a custom box wrap style.
     *     Ext.get("foo").boxWrap().addCls("x-box-blue");
     *
     * @param {String} [class='x-box'] A base CSS class to apply to the containing wrapper element.
     * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
     * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
     * @return {Ext.dom.Element} The outermost wrapping element of the created box structure.
     */
    boxWrap : function(cls) {
        cls = cls || Ext.baseCSSPrefix + 'box';
        var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Element.boxMarkup, cls) + "</div>"));
        Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
        return el;
    },

    /**
     * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
     * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
     * if a height has not been set using CSS.
     * @return {Number}
     */
    getComputedHeight : function() {
        var me = this,
            h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
        if (!h) {
            h = parseFloat(me.getStyle(HEIGHT)) || 0;
            if (!me.isBorderBox()) {
                h += me.getFrameWidth('tb');
            }
        }
        return h;
    },

    /**
     * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
     * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
     * if a width has not been set using CSS.
     * @return {Number}
     */
    getComputedWidth : function() {
        var me = this,
            w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);

        if (!w) {
            w = parseFloat(me.getStyle(WIDTH)) || 0;
            if (!me.isBorderBox()) {
                w += me.getFrameWidth('lr');
            }
        }
        return w;
    },

    /**
     * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
     * for more information about the sides.
     * @param {String} sides
     * @return {Number}
     */
    getFrameWidth : function(sides, onlyContentBox) {
        return (onlyContentBox && this.isBorderBox()) ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
    },

    /**
     * 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,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.hover(
            function() {
                if (hasTest && testFn.call(scope || me, me) === false) {
                    return;
                }
                Ext.fly(dom, INTERNAL).addCls(className);
            },
            function() {
                Ext.fly(dom, INTERNAL).removeCls(className);
            }
        );
        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,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("focus", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
        });
        me.on("blur", function() {
            Ext.fly(dom, INTERNAL).removeCls(className);
        });
        return me;
    },

    /**
     * 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,
            dom = me.dom,
            hasTest = Ext.isFunction(testFn);
            
        me.on("mousedown", function() {
            if (hasTest && testFn.call(scope || me, me) === false) {
                return false;
            }
            Ext.fly(dom, INTERNAL).addCls(className);
            var d = Ext.getDoc(),
                fn = function() {
                    Ext.fly(dom, INTERNAL).removeCls(className);
                    d.removeListener("mouseup", fn);
                };
            d.on("mouseup", fn);
        });
        return me;
    },

    /**
     * Returns the dimensions of the element available to lay content out in.
     *
     * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and
     * offsetWidth/clientWidth. To obtain the size excluding scrollbars, use getViewSize.
     *
     * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
     *
     * @return {Object} Object describing width and height.
     * @return {Number} return.width
     * @return {Number} return.height
     */
    getStyleSize : function() {
        var me = this,
            d = this.dom,
            isDoc = DOCORBODYRE.test(d.nodeName),
            s ,
            w, h;

        // If the body, use static methods
        if (isDoc) {
            return {
                width : Element.getViewWidth(),
                height : Element.getViewHeight()
            };
        }

        s = me.getStyle([HEIGHT, WIDTH], true);  //seek inline
        // Use Styles if they are set
        if (s.width && s.width != 'auto') {
            w = parseFloat(s.width);
            if (me.isBorderBox()) {
                w -= me.getFrameWidth('lr');
            }
        }
        // Use Styles if they are set
        if (s.height && s.height != 'auto') {
            h = parseFloat(s.height);
            if (me.isBorderBox()) {
                h -= me.getFrameWidth('tb');
            }
        }
        // Use getWidth/getHeight if style not set.
        return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
    },

    statics: {
        selectableCls: Ext.baseCSSPrefix + 'selectable',
        unselectableCls: Ext.baseCSSPrefix + 'unselectable'
    },

    /**
     * Enable text selection for this element (normalized across browsers)
     * @return {Ext.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;
    },

    /**
     * Changes this Element's state to "vertical" (rotated 90 or 270 degrees).
     * This involves inverting the getters and setters for height and width,
     * and applying hooks for rotating getters and setters for border/margin/padding.
     * (getWidth becomes getHeight and vice versa), setStyle and getStyle will
     * also return the inverse when height or width are being operated on.
     * 
     * @param {Number} angle the angle of rotation - either 90 or 270
     * @param {String} cls an optional css class that contains the required
     * styles for switching the element to vertical orientation. Omit this if
     * the element already contains vertical styling.  If cls is provided,
     * it will be removed from the element when {@link #setHorizontal} is called.
     * @private
     */
    setVertical: function(angle, cls) {
        var me = this,
            proto = Element.prototype,
            hooks;

        me.vertical = true;
        if (cls) {
            me.addCls(me.verticalCls = cls);
        }

        me.setWidth = proto.setHeight;
        me.setHeight = proto.setWidth;
        if (!Ext.isIE9m) {
            // In browsers that use CSS3 transforms we must invert getHeight and
            // get Width. In IE9 and below no adjustment is needed because we use
            // a BasicImage filter to rotate the element and the element's
            // offsetWidth and offsetHeight are automatically inverted.
            me.getWidth = proto.getHeight;
            me.getHeight = proto.getWidth;
        }

        // Switch to using the appropriate vertical style hooks
        me.styleHooks = (angle === 270) ?
            Element.prototype.verticalStyleHooks270 : Element.prototype.verticalStyleHooks90;
    },

    /**
     * Removes "vertical" state from this element (reverses everything done
     * by {@link #setVertical}).
     * @private
     */
    setHorizontal: function() {
        var me = this,
            cls = me.verticalCls;

        delete me.vertical;
        if (cls) {
            delete me.verticalCls;
            me.removeCls(cls);
        }

        // delete the inverted methods and revert to inheriting from the prototype 
        delete me.setWidth;
        delete me.setHeight;
        if (!Ext.isIE9m) {
            delete me.getWidth;
            delete me.getHeight;
        }

        // revert to inheriting styleHooks from the prototype
        delete me.styleHooks;
    }
});

Element.prototype.styleHooks = styleHooks = Ext.dom.AbstractElement.prototype.styleHooks;

// When elements are rotated 80 or 270 degrees, their border, margin and padding hooks
// need to be rotated as well.
Element.prototype.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(Element.prototype.styleHooks);
Element.prototype.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(Element.prototype.styleHooks);

verticalStyleHooks90.width = { name: 'height' };
verticalStyleHooks90.height = { name: 'width' };
verticalStyleHooks90['margin-top'] = { name: 'marginLeft' };
verticalStyleHooks90['margin-right'] = { name: 'marginTop' };
verticalStyleHooks90['margin-bottom'] = { name: 'marginRight' };
verticalStyleHooks90['margin-left'] = { name: 'marginBottom' };
verticalStyleHooks90['padding-top'] = { name: 'paddingLeft' };
verticalStyleHooks90['padding-right'] = { name: 'paddingTop' };
verticalStyleHooks90['padding-bottom'] = { name: 'paddingRight' };
verticalStyleHooks90['padding-left'] = { name: 'paddingBottom' };
verticalStyleHooks90['border-top'] = { name: 'borderLeft' };
verticalStyleHooks90['border-right'] = { name: 'borderTop' };
verticalStyleHooks90['border-bottom'] = { name: 'borderRight' };
verticalStyleHooks90['border-left'] = { name: 'borderBottom' };

verticalStyleHooks270.width = { name: 'height' };
verticalStyleHooks270.height = { name: 'width' };
verticalStyleHooks270['margin-top'] = { name: 'marginRight' };
verticalStyleHooks270['margin-right'] = { name: 'marginBottom' };
verticalStyleHooks270['margin-bottom'] = { name: 'marginLeft' };
verticalStyleHooks270['margin-left'] = { name: 'marginTop' };
verticalStyleHooks270['padding-top'] = { name: 'paddingRight' };
verticalStyleHooks270['padding-right'] = { name: 'paddingBottom' };
verticalStyleHooks270['padding-bottom'] = { name: 'paddingLeft' };
verticalStyleHooks270['padding-left'] = { name: 'paddingTop' };
verticalStyleHooks270['border-top'] = { name: 'borderRight' };
verticalStyleHooks270['border-right'] = { name: 'borderBottom' };
verticalStyleHooks270['border-bottom'] = { name: 'borderLeft' };
verticalStyleHooks270['border-left'] = { name: 'borderTop' };

if (Ext.isIE7m) {
    styleHooks.fontSize = styleHooks['font-size'] = {
        name: 'fontSize',
        canThrow: true
    };
    
    styleHooks.fontStyle = styleHooks['font-style'] = {
        name: 'fontStyle',
        canThrow: true
    };
    
    styleHooks.fontFamily = styleHooks['font-family'] = {
        name: 'fontFamily',
        canThrow: true
    };
}

// override getStyle for border-*-width
if (Ext.isIEQuirks || Ext.isIE && Ext.ieVersion <= 8) {
    function getBorderWidth (dom, el, inline, style) {
        if (style[this.styleName] == 'none') {
            return '0px';
        }
        return style[this.name];
    }

    edges = ['Top','Right','Bottom','Left'];
    k = edges.length;

    while (k--) {
        edge = edges[k];
        borderWidth = 'border' + edge + 'Width';

        styleHooks['border-'+edge.toLowerCase()+'-width'] = styleHooks[borderWidth] = {
            name: borderWidth,
            styleName: 'border' + edge + 'Style',
            get: getBorderWidth
        };
    }
}

// The following hack is needed to support padding on dom elements with display:table.
// It was added because at one point auto layout's "outerCt" element had padding applied
// to it. The padding is now appplied to the innerCt which is display:table-cell, so this
// hack is not currently needed.
//if (Ext.isIE9 && Ext.isStrict) {
//    // In IE9, getComputedStyle always returns 0px for padding if the element has
//    // "display:table", so we use currentStyle instead.
//    var names = {
//            padding: 'padding',
//            paddingTop: 'padding-top',
//            paddingRight: 'padding-right',
//            paddingBottom: 'padding-bottom',
//            paddingLeft: 'padding-left'
//        },
//        createHook = function(name, camelCaseName) {
//            styleHooks[name] = styleHooks[camelCaseName] = {
//                name: name,
//                get: function(dom) {
//                    return dom.currentStyle[name];
//                }
//            }
//        },
//        camelCaseName;
//
//    for (camelCaseName in names) {
//        createHook(names[camelCaseName], camelCaseName);
//    }
//}

// Element.unselectable relies on this listener to prevent selection in IE. Some other browsers support the event too
// but it is only strictly required for IE. In WebKit this listener causes subtle differences to how the browser handles
// the non-selection, e.g. whether or not the mouse cursor changes when attempting to select text.
Ext.getDoc().on('selectstart', function(ev, dom) {
    var doc = document.documentElement,
        selectableCls = Element.selectableCls,
        unselectableCls = Element.unselectableCls,
        tagName = dom && dom.tagName;

    tagName = tagName && tagName.toLowerCase();

    // Element.unselectable is not really intended to handle selection within text fields and it is important that
    // fields inside menus or panel headers don't inherit the unselectability. In most browsers this is automatic but in
    // IE 9 the selectstart event can bubble up from text fields so we have to explicitly handle that case.
    if (tagName === 'input' || tagName === 'textarea') {
        return;
    }

    // Walk up the DOM checking the nodes. This may be 'slow' but selectstart events don't fire very often
    while (dom && dom.nodeType === 1 && dom !== doc) {
        var el = Ext.fly(dom);

        // If the node has the class x-selectable then stop looking, the text selection is allowed
        if (el.hasCls(selectableCls)) {
            return;
        }

        // If the node has class x-unselectable then the text selection needs to be stopped
        if (el.hasCls(unselectableCls)) {
            ev.stopEvent();
            return;
        }

        dom = dom.parentNode;
    }
});

});

Ext.onReady(function () {
    var opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        hooks = Ext.dom.Element.prototype.styleHooks;

    // Ext.supports flags are not populated until onReady...
    hooks.opacity = {
        name: 'opacity',
        afterSet: function(dom, value, el) {
            if (el.isLayer) {
                el.onOpacitySet(value);
            }
        }
    };
    if (!Ext.supports.Opacity && Ext.isIE) {
        Ext.apply(hooks.opacity, {
            get: function (dom) {
                var filter = dom.style.filter,
                    match, opacity;
                if (filter.match) {
                    match = filter.match(opacityRe);
                    if (match) {
                        opacity = parseFloat(match[1]);
                        if (!isNaN(opacity)) {
                            return opacity ? opacity / 100 : 0;
                        }
                    }
                }
                return 1;
            },
            set: function (dom, value) {
                var style = dom.style,
                    val = style.filter.replace(opacityRe, '').replace(trimRe, '');

                style.zoom = 1; // ensure dom.hasLayout

                // value can be a number or '' or null... so treat falsey as no opacity
                if (typeof(value) == 'number' && value >= 0 && value < 1) {
                    value *= 100;
                    style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity='+value+')';
                } else {
                    style.filter = val;
                }
            }  
        });
    }
    // else there is no work around for the lack of opacity support. Should not be a
    // problem given that this has been supported for a long time now...
});
