com.sencha.gxt.core.client.dom.XElement.java Source code

Java tutorial

Introduction

Here is the source code for com.sencha.gxt.core.client.dom.XElement.java

Source

/**
 * Sencha GXT 4.0.0 - Sencha for GWT
 * Copyright (c) 2006-2015, Sencha Inc.
 *
 * licensing@sencha.com
 * http://www.sencha.com/products/gxt/license/
 *
 * ================================================================================
 * Open Source License
 * ================================================================================
 * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3
 * license. You may use this license only if you are prepared to distribute and
 * share the source code of your application under the GPL v3 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * If you are NOT prepared to distribute and share the source code of your
 * application under the GPL v3 license, other commercial and oem licenses
 * are available for an alternate download of Sencha GXT.
 *
 * Please see the Sencha GXT Licensing page at:
 * http://www.sencha.com/products/gxt/license/
 *
 * For clarification or additional options, please contact:
 * licensing@sencha.com
 * ================================================================================
 *
 *
 * ================================================================================
 * Disclaimer
 * ================================================================================
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 * ================================================================================
 */
package com.sencha.gxt.core.client.dom;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.DOM;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.Style;
import com.sencha.gxt.core.client.Style.Anchor;
import com.sencha.gxt.core.client.Style.AnchorAlignment;
import com.sencha.gxt.core.client.Style.ScrollDirection;
import com.sencha.gxt.core.client.Style.Side;
import com.sencha.gxt.core.client.dom.impl.ComputedStyleImpl;
import com.sencha.gxt.core.client.resources.CommonStyles;
import com.sencha.gxt.core.client.resources.ThemeStyles;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.core.client.util.Padding;
import com.sencha.gxt.core.client.util.Point;
import com.sencha.gxt.core.client.util.Rectangle;
import com.sencha.gxt.core.client.util.Region;
import com.sencha.gxt.core.client.util.Scroll;
import com.sencha.gxt.core.client.util.Size;
import com.sencha.gxt.core.client.util.TextMetrics;
import com.sencha.gxt.core.client.util.Util;
import com.sencha.gxt.core.shared.FastMap;

/**
 * Adds additional features and functionality to the GWT <code>Element</code> class.
 * 
 * <p />
 * XElement's cannot be directly instantiated. New XElements can be created using the static
 * {@link #createElement(String)} method.
 */
@SuppressWarnings("deprecation")
public class XElement extends com.google.gwt.user.client.Element {

    /**
     * VisMode enumeration. Specifies the the element should hidden using the CSS display or visibility style.
     * 
     * @see #setVisible(boolean)
     */
    public enum VisMode {
        DISPLAY, VISIBILITY
    }

    /**
     * Workaround for http://code.google.com/p/google-web-toolkit/issues/detail?id=3192, breaking the XElement static
     * fields into their own class so they can be created, accessed through a 'real' Java type, not a JSO.
     */
    private static class FieldHolder {
        private static Map<String, Boolean> borderBoxMap = new FastMap<Boolean>();

        private static ComputedStyleImpl computedStyle = GWT.create(ComputedStyleImpl.class);

        private static int adjustXYOffset;

        private static RegExp leftOrRight = RegExp.compile("Left|Right");

        static {
            XElement elem = XElement.createElement("div");
            elem.setXY(100, 100);

            Document.get().getBody().appendChild(elem);

            Point p = elem.getXY();
            if (p.getX() != 100) {
                adjustXYOffset = p.getX() - 100;
            }

            elem.removeFromParent();
        }
    }

    /**
     * Tests to see if the value has units, otherwise appends the default (px).
     * 
     * @param v the value
     * @return the value with units
     */
    public native static String addUnits(String v, String defaultUnit) /*-{
                                                                       if (v === "" || v == "auto") {
                                                                       return v;
                                                                       }
                                                                       if (v === undefined) {
                                                                       return '';
                                                                       }
                                                                       if (typeof v == "number"
                                                                       || !/\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i.test(v)) {
                                                                       return v + (defaultUnit || 'px');
                                                                       }
                                                                       return v;
                                                                       }-*/;

    /**
     * Assert that the given {@link Node} is an {@link Element} and automatically typecast it.
     */
    public static XElement as(Node node) {
        assert Element.is(node);
        return (XElement) node;
    }

    /**
     * Creates a new element based on the specified HTML tag name.
     * 
     * @param tagName the element tag name
     * @return the new element
     */
    public static final XElement createElement(String tagName) {
        return (XElement) Document.get().createElement(tagName);
    }

    /**
     * Returns true if the passed element has a border box.
     * 
     * @param element the element to test
     * @return true if the passed element has a border box
     */
    public static boolean isBorderBox(Element element) {
        assert element != null : "Element may not be null";
        String tag = element.getTagName().toLowerCase();
        Boolean r = FieldHolder.borderBoxMap.get(tag);
        if (r == null) {
            Element testElement = (Element) Document.get().createElement(tag);
            testElement.getStyle().setPropertyPx("padding", 1);
            testElement.getStyle().setPropertyPx("width", 100);
            testElement.getStyle().setProperty("visibility", "hidden");
            testElement.getStyle().setProperty("position", "absolute");
            Document.get().getBody().appendChild(testElement);
            r = testElement.getOffsetWidth() == 100;
            Document.get().getBody().removeChild(testElement);
            FieldHolder.borderBoxMap.put(tag, r);
        }
        return r;
    }

    private native static void disableTextSelectInternal(Element e, boolean disable) /*-{
                                                                                     if (disable) {
                                                                                     e.ondrag = function(evt) {
                                                                                     var targ;
                                                                                     if (!evt)
                                                                                     evt = $wnd.event;
                                                                                     if (evt.target)
                                                                                     targ = evt.target;
                                                                                     else if (evt.srcElement)
                                                                                     targ = evt.srcElement;
                                                                                     if (targ.nodeType == 3) // defeat Safari bug
                                                                                     targ = targ.parentNode;
                                                                                     if (targ.tagName == 'INPUT' || targ.tagName == 'TEXTAREA') {
                                                                                     return true;
                                                                                     }
                                                                                     return false;
                                                                                     }
                                                                                     e.onselectstart = function(evt) {
                                                                                     var targ;
                                                                                     if (!evt)
                                                                                     evt = $wnd.event;
                                                                                     if (evt.target)
                                                                                     targ = evt.target;
                                                                                     else if (evt.srcElement)
                                                                                     targ = evt.srcElement;
                                                                                     if (targ.nodeType == 3) // defeat Safari bug
                                                                                     targ = targ.parentNode;
                                                                                     if (targ.tagName == 'INPUT' || targ.tagName == 'TEXTAREA') {
                                                                                     return true;
                                                                                     }
                                                                                     return false;
                                                                                     };
                                                                                     } else {
                                                                                     e.ondrag = null;
                                                                                     e.onselectstart = null;
                                                                                     }
                                                                                     }-*/;

    /**
     * Not directly instantiable. Subclasses should also define a protected no-arg constructor to prevent client code from
     * directly instantiating the class.
     */
    protected XElement() {
    }

    /**
     * Adds the style names to the element. Duplicate styles are automatically filtered out.
     * 
     * @param classNames the new class names to add
     */
    public final void addClassName(String... classNames) {
        if (classNames != null) {
            for (String styleName : classNames) {
                if (styleName != null && !hasClassName(styleName)) {
                    styleName = styleName.trim();
                    setClassName(getClassName() + " " + styleName);
                }
            }
        }
    }

    /**
     * Adds the style names to the element. Duplicate styles are automatically filtered out.
     * 
     * @param className1 the first new class name to add
     * @param className2 the second new class name to add
     */
    public final void addClassName(String className1, String className2) {
        addClassName(className1);
        addClassName(className2);
    }

    /**
     * Adds the event type to the element's sunk events.
     * 
     * @param event the events to add
     */
    public final void addEventsSunk(int event) {
        int bits = DOM.getEventsSunk((Element) this.cast());
        DOM.sinkEvents((Element) this.cast(), bits | event);
    }

    /**
     * Ensures the entire element fits within the browser viewport.
     * The target point (x, y) will be adjusted inward until it fits.
     * This may result in a different position, but the size will
     * stay the same.
     * 
     * @param point the target point
     * @return the adjusted point
     */
    public final Point adjustForConstraints(Point point) {
        return adjustForConstraints(Document.get().getBody(), point);
    }

    /**
     * Ensures the entire element fits within the constraining element.
     * This may result in a different position, but the size will
     * stay the same.
     *
     * @param constraint the element to constrain within
     * @param point the target point
     * @return the adjusted point
     */
    public final Point adjustForConstraints(Element constraint, Point point) {
        // possibly move logic here and deprecate protected method
        return getConstrainToXY(constraint, point);
    }

    /**
     * Ensures the entire element fits within the browser viewport.
     * The target bounds (x, y, width, height) will be adjusted inward
     * until it fits. This may result in a both a different position
     * and a different size.
     *
     * @param bounds the target bounds
     * @return the adjusted bounds
     */
    public final Rectangle adjustForConstraints(Rectangle bounds) {
        return adjustForConstraints(Document.get().getBody(), bounds);
    }

    /**
     * Ensures the entire element fits within the constraining element.
     * The target bounds (x, y, width, height) will be adjusted inward
     * until it fits. This may result in a both a different position
     * and a different size.
     *
     * @param constraint the element to constrain within
     * @param bounds the target bounds
     * @return the adjusted bounds
     */
    public final Rectangle adjustForConstraints(Element constraint, Rectangle bounds) {
        int constraintTop = 0;
        int constraintRight = 0;
        int constraintBottom = 0;
        int constraintLeft = 0;
        if (constraint == Document.get().getBody()) {
            constraintRight = XDOM.getViewportSize().getWidth();
            constraintBottom = XDOM.getViewportSize().getHeight();
        } else {
            constraintTop = constraint.getAbsoluteTop();
            constraintRight = constraint.getAbsoluteLeft() + constraint.getOffsetWidth();
            constraintBottom = constraint.getAbsoluteTop() + constraint.getOffsetHeight();
            constraintLeft = constraint.getAbsoluteLeft();
        }

        int boundsTop = bounds.getY();
        int boundsRight = bounds.getX() + bounds.getWidth();
        int boundsBottom = bounds.getY() + bounds.getHeight();
        int boundsLeft = bounds.getX();

        if (boundsTop < constraintTop) {
            boundsTop = constraintTop;
        }
        if (boundsRight > constraintRight) {
            boundsRight = constraintRight;
        }
        if (boundsBottom > constraintBottom) {
            boundsBottom = constraintBottom;
        }
        if (boundsLeft < constraintLeft) {
            boundsLeft = constraintLeft;
        }

        return new Rectangle(boundsLeft, boundsTop, (boundsRight - boundsLeft), (boundsBottom - boundsTop));
    }

    /**
     * Aligns the element with another element relative to the specified anchor points.
     * 
     * @param elem the element to align to
     * @param alignment the position to align to
     * @param offsetX X offset
     * @param offsetY Y offset
     */
    public final void alignTo(Element elem, AnchorAlignment alignment, int offsetX, int offsetY) {
        Point p = getAlignToXY(elem, alignment, offsetX, offsetY);
        setXY(p);
    }

    /**
     * Sets multiple style properties. Style attribute names must be in lower camel case, e.g.
     * "backgroundColor:white; color:red;"
     * 
     * @param styles a style specification string
     */
    public final native void applyStyles(String styles) /*-{
                                                        var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
                                                        var matches;
                                                        while ((matches = re.exec(styles)) != null) {
                                                        this.style[matches[1]] = matches[2];
                                                        }
                                                        }-*/;

    /**
     * Centers the element in the viewport.
     */
    public final void center() {
        center(null);
    }

    /**
     * Centers the element.
     * 
     * @param constrainViewport true to constrain the element position to the viewport.
     */
    public final void center(boolean constrainViewport) {
        alignTo(Document.get().getBody(), new AnchorAlignment(Anchor.CENTER, Anchor.CENTER, constrainViewport), 0,
                0);
    }

    /**
     * Centers an element.
     * 
     * @param container the container element
     */
    public final void center(Element container) {
        if (container == null) {
            container = Document.get().getBody();
        }
        alignTo(container, new AnchorAlignment(Anchor.CENTER, Anchor.CENTER, false), 0, 0);
    }

    /**
     * Selects a single child at any depth below this element based on the passed CSS selector.
     * 
     * @param selector the css selector
     * @return the child element
     */
    public final XElement child(String selector) {
        Element child = childElement(selector);
        return child == null ? null : XElement.as(child);
    }

    /**
     * Selects a single child at any depth below this element based on the passed CSS selector.
     * 
     * @param selector the css selector
     * @return the child element
     */
    public final Element childElement(String selector) {
        return DomQuery.selectNode(selector, this);
    }

    /**
     * Generators a native dom click on the element.
     */
    public final native void click() /*-{
                                     var dom = this;
                                     if (dom.click) {
                                     dom.click();
                                     } else {
                                     var event = $doc.createEvent("MouseEvents");
                                     event.initEvent('click', true, true, $wnd, 0, 0, 0, 0, 0, false,
                                     false, false, false, 1, dom);
                                     dom.dispatchEvent(event);
                                     }
                                     }-*/;

    /**
     * Creates and adds a child using the HTML fragment.
     *
     * @param html the html fragment
     * @return the new child
     */
    public final XElement createChild(SafeHtml html) {
        return appendChild(XDOM.create(html)).<XElement>cast();
    }

    /**
     * Disables the element.
     */
    public final void disable() {
        setPropertyBoolean("disabled", true);
    }

    /**
     * Enables or disables text selection for the element and all children. A circular reference will be created when disabling text
     * selection. Disabling should be cleared when the element is detached. See the <code>Component</code> source for an
     * example.
     * 
     * @param disable true to disable, false to enable
     */
    public final void disableTextSelection(boolean disable) {
        setClassName(CommonStyles.get().unselectable(), disable);
        setPropertyString("unselectable", disable ? "on" : "");
        disableTextSelectInternal(this, disable);
    }

    /**
     * Enables or disables text selection for the element. A circular reference will be created when disabling text
     * selection. Disabling should be cleared when the element is detached. See the <code>Component</code> source for an
     * example.
     *
     * @param disable true to disable, false to enable
     */
    public final void disableTextSelectionSingle(boolean disable) {
        setClassName(CommonStyles.get().unselectableSingle(), disable);
        setPropertyString("unselectable", disable ? "on" : "");
        disableTextSelectInternal(this, disable);
    }

    /**
     * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
     * 
     * @param selector the CSS selector
     * @return the child element
     */
    public final XElement down(String selector) {
        Element elem = DomQuery.selectNode(" > " + selector, this);
        if (elem != null) {
            return XElement.as(elem);
        }
        return null;
    }

    /**
     * Enables the element.
     */
    public final void enable() {
        setPropertyBoolean("disabled", false);
    }

    /**
     * Walks up the DOM and ensures all elements are visible. Useful when trying to calculate offsets or page coordinates.
     * 
     * @return list of meta data, see {@link #restoreVisible(List)}
     */
    public final List<FastMap<Object>> ensureVisible() {
        List<FastMap<Object>> list = new ArrayList<FastMap<Object>>();
        XElement p = this;
        XElement body = Document.get().getBody().cast();
        while (p != null && p != body) {
            if (p.isStyleAttribute("display", "none")) {
                FastMap<Object> m = new FastMap<Object>();
                m.put("element", p);
                m.put("origd", p.getStyle().getProperty("display"));

                boolean hasxhideoffset = p.hasClassName(CommonStyles.get().hideOffsets());
                m.put("hasxhideoffset", hasxhideoffset);
                if (!hasxhideoffset) {
                    p.addClassName(CommonStyles.get().hideOffsets());
                }

                boolean hideDisplay = p.hasClassName(CommonStyles.get().hideDisplay());
                if (hideDisplay) {
                    m.put("hasxhidedisplay", hideDisplay);
                    p.removeClassName(CommonStyles.get().hideDisplay());
                }

                p.getStyle().setProperty("display", "block");
                list.add(m);
            }
            p = (XElement) p.getParentElement();
        }
        return list.size() > 0 ? list : null;
    }

    /**
     * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or
     * span:first-child).
     * 
     * @param selector the simple selector to test
     * @return the matching element
     */
    public final XElement findParent(String selector, int maxDepth) {
        Element elem = findParentElement(selector, maxDepth);
        if (elem == null) {
            return null;
        }
        return XElement.as(elem);
    }

    /**
     * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or
     * span:first-child).
     * 
     * @param selector the simple selector to test
     * @param maxDepth the max depth
     * @return the matching element
     */
    public final Element findParentElement(String selector, int maxDepth) {
        Element p = this;
        Element b = Document.get().getBody();
        int depth = 0;
        while (p != null && p.getNodeType() == 1 && (maxDepth == -1 || depth < maxDepth) && p != b) {
            if (DomQuery.is(p, selector)) {
                return p;
            }
            depth++;
            p = (Element) p.getParentElement();
        }
        return null;
    }

    /**
     * Gets the x,y coordinates to align this element with another element.
     * 
     * @param elem the element to align to
     * @param alignment the alignment
     * @param ox x offset
     * @param oy y offset
     * @return the point
     */
    public final Point getAlignToXY(Element elem, AnchorAlignment alignment, int ox, int oy) {
        XElement el = XElement.as(elem);

        boolean constrainViewport = alignment.isConstrainViewport();
        Anchor anch1 = alignment.getAlign();
        Anchor anch2 = alignment.getTargetAlign();
        // Subtract the aligned el's internal xy from the target's offset xy
        // plus custom offset to get the aligned el's new offset xy
        Point a1 = getAnchorXY(anch1, true);
        Point a2 = el.getAnchorXY(anch2, false);

        int x = a2.getX() - a1.getX() + ox;
        int y = a2.getY() - a1.getY() + oy;

        if (constrainViewport) {
            // constrain the aligned el to viewport if necessary
            int w = getOffsetWidth();
            int h = getOffsetHeight();
            Region r = el.getRegion();
            int dw = XDOM.getViewWidth(false);
            int dh = XDOM.getViewHeight(false);

            // If we are at a viewport boundary and the aligned el is anchored on a
            // target border that is
            // perpendicular to the vp border, allow the aligned el to slide on that
            // border,
            // otherwise swap the aligned el to the opposite border of the target.
            boolean swapY = (anch1.isTop() && anch2.isBottom()) || (anch1.isBottom() && anch2.isTop());
            boolean swapX = (anch1.isRight() && anch2.isLeft()) || (anch1.isLeft() && anch2.isRight());

            // EXTGWT-1730 only applying 5 when there is scrolling. 5 may be able to
            // be removed
            // but not sure why this was added in first place. it exists in 2.0
            int scrollX = XDOM.getBodyScrollLeft();

            if (scrollX > 0) {
                scrollX += 5;
            }
            int scrollY = XDOM.getBodyScrollTop();
            if (scrollY > 0) {
                scrollY += 5;
            }

            if ((x + w) > dw + scrollX) {
                x = swapX ? r.getLeft() - w : dw + scrollX - w;
            }
            if (x < scrollX) {
                x = swapX ? r.getRight() : scrollX;
            }

            if ((y + h) > (dh + scrollY)) {
                y = swapY ? r.getTop() - h : dh + scrollY - h;
            }
            if (y < scrollY) {
                y = swapY ? r.getBottom() : scrollY;
            }
        }

        return new Point(x, y);
    }

    /**
     * Returns the x,y coordinates specified by the anchor position on the element.
     * 
     * @param anchor the specified anchor position (defaults to {@code CENTER}). See {@link #alignTo} for details on
     *          supported anchor positions.
     * @param local {@code true} to get the local (element top/left-relative) anchor position instead of page coordinates
     * @return the position
     */
    public final Point getAnchorXY(Anchor anchor, boolean local) {
        if (anchor == null) {
            return null;
        }
        boolean vp = false;
        final int w;
        final int h;
        if (this.cast() == Document.get().getBody() || this == Document.get().getDocumentElement()) {
            vp = true;
            w = XDOM.getViewWidth(false);
            h = XDOM.getViewHeight(false);
        } else {
            w = getOffsetWidth();
            h = getOffsetHeight();
        }

        int x = 0, y = 0;
        switch (anchor) {
        case CENTER:
            x = (int) Math.round(w * .5);
            y = (int) Math.round(h * .5);
            break;
        case TOP:
            x = (int) Math.round(w * .5);
            y = 0;
            break;
        case LEFT:
            x = 0;
            y = (int) Math.round(h * .5);
            break;
        case RIGHT:
            x = w;
            y = (int) Math.round(h * .5);
            break;
        case BOTTOM:
            x = (int) Math.round(w * .5);
            y = h;
            break;
        case TOP_LEFT:
            x = 0;
            y = 0;
            break;
        case BOTTOM_LEFT:
            x = 0;
            y = h;
            break;
        case BOTTOM_RIGHT:
            x = w;
            y = h;
            break;
        case TOP_RIGHT:
            x = w;
            y = 0;
            break;
        }

        if (local) {
            return new Point(x, y);
        }
        if (vp) {
            Scroll sc = getScroll();
            return new Point(x + sc.getScrollLeft(), y + sc.getScrollTop());
        }
        // Add the element's offset xy

        Point o = getXY();
        return new Point(x + o.getX(), y + o.getY());
    }

    /**
     * Returns the total border size of the specified sides.
     * <p/>
     * Passing more than one side will yield the sum of the sides of those sides of the element.
     * 
     * @param side the side
     * @return the sum of the widths of the borders
     */
    public final int getBorders(Side side) {
        switch (side) {
        case LEFT:
            return Util.parseInt(getComputedStyle("borderLeft"), 0);
        case RIGHT:
            return Util.parseInt(getComputedStyle("borderRight"), 0);
        case TOP:
            return Util.parseInt(getComputedStyle("borderTop"), 0);
        case BOTTOM:
            return Util.parseInt(getComputedStyle("borderBottom"), 0);
        }
        assert false : "Unparsable Side " + side;
        return 0;
    }

    /**
     * Returns the total border size of the specified sides.
     * <p/>
     * Passing more than one side will yield the sum of the sides of those sides of the element.
     * 
     * @param sides the sides
     * @return the sum of the widths of the borders
     */
    public final int getBorders(Side... sides) {
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < sides.length; i++) {
            Side side = sides[i];
            appendSideToList(list, side, "border");
        }

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the total border size of the specified sides.
     * <p/>
     * Passing more than one side will yield the sum of the sides of those sides of the element.
     * 
     * @param side1 the first side
     * @param side2 the second side
     * @return the sum of the widths of the borders
     */
    public final int getBorders(Side side1, Side side2) {
        List<String> list = new ArrayList<String>();

        appendSideToList(list, side1, "border");
        appendSideToList(list, side2, "border");

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the elements bounds in page coordinates.
     * 
     * @return the bounds
     */
    public final Rectangle getBounds() {
        return getBounds(false, false);
    }

    /**
     * Returns the elements bounds in page coordinates.
     * 
     * @param local if true the element's left and top are returned instead of page coordinates
     * 
     * @return the bounds
     */
    public final Rectangle getBounds(boolean local) {
        return getBounds(local, false);
    }

    /**
     * Returns the element's bounds in page coordinates.
     * 
     * @param local if true the element's left and top are returned instead of page coordinates
     * @param adjust if true sizes get adjusted
     * 
     * @return the element's bounds
     */
    public final Rectangle getBounds(boolean local, boolean adjust) {
        Size s = getSize(adjust);
        Rectangle rect = new Rectangle();
        rect.setWidth(s.getWidth());
        rect.setHeight(s.getHeight());
        if (local) {
            rect.setX(getLeft(true));
            rect.setY(getTop(true));
        } else {
            Point p = getXY();
            rect.setX(p.getX());
            rect.setY(p.getY());
        }
        return rect;
    }

    /**
     * Returns the index of the child element.
     * 
     * @return the index
     */
    public final int getChildIndex(Element child) {
        return DOM.getChildIndex((Element) this.cast(), (Element) child.cast());
    }

    /**
     * Returns either the offsetHeight or the height of this element based on it's CSS height.
     * 
     * @return the height
     */
    public final int getComputedHeight() {
        int h = getOffsetHeight();
        if (h == 0) {
            h = getStyleSize().getHeight();
        }
        return h;
    }

    /**
     * Returns a map of style values mapped by property name.
     * 
     * @param props the list of CSS property names
     * @return the map of property and values
     */
    public final FastMap<String> getComputedStyle(List<String> props) {
        return FieldHolder.computedStyle.getStyleAttribute(this, props);
    }

    /**
     * Normalizes currentStyle and computedStyle.
     * 
     * @param prop the style attribute whose value is returned.
     * @return the current value of the style attribute for this element.
     */
    public final String getComputedStyle(String prop) {
        return FieldHolder.computedStyle.getStyleAttribute(this, prop);
    }

    /**
     * Returns either the offsetWidth or the width of this element based on it's CSS width.
     * 
     * @return the width
     */
    public final int getComputedWidth() {
        int w = getOffsetWidth();
        if (w == 0) {
            w = getStyleSize().getWidth();
        }
        return w;
    }

    /**
     * Returns the sum width of the padding and borders for all "sides". See #getBorderWidth() for more information about
     * the sides.
     * 
     * @return the frame size
     */
    public final Size getFrameSize() {
        int width = 0;
        int height = 0;
        List<String> list = new ArrayList<String>();
        list.add("paddingLeft");
        list.add("borderLeftWidth");

        list.add("paddingRight");
        list.add("borderRightWidth");

        list.add("paddingTop");
        list.add("borderTopWidth");

        list.add("paddingBottom");
        list.add("borderBottomWidth");

        FastMap<String> map = getComputedStyle(list);
        for (String s : map.keySet()) {
            if (isLeftOrRight(s)) {
                width += Util.parseInt(map.get(s), 0);
            } else {
                height += Util.parseInt(map.get(s), 0);
            }
        }
        return new Size(width, height);
    }

    /**
     * Returns the sum width of the padding and borders for the passed "sides".
     * 
     * @param side the sides
     * @return the width
     */
    public final int getFrameWidth(Side... side) {
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < side.length; i++) {
            Side s = side[i];
            appendSideToList(list, s, "padding");
            appendSideToList(list, s, "border");
        }

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the sum width of the padding and borders for the given side.
     * 
     * @param side the side
     * @return the total width
     */
    public final int getFrameWidth(Side side) {
        List<String> list = new ArrayList<String>();

        appendSideToList(list, side, "padding");
        appendSideToList(list, side, "border");
        return getStylePropertyTotal(list);
    }

    /**
     * Returns the sum width of the padding and borders for the given side.
     * 
     * @param side1 the side
     * @param side2 the side
     * @return the total width
     */
    public final int getFrameWidth(Side side1, Side side2) {
        List<String> list = new ArrayList<String>();

        appendSideToList(list, side1, "padding");
        appendSideToList(list, side1, "border");
        appendSideToList(list, side2, "padding");
        appendSideToList(list, side2, "border");

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the element's height.
     * 
     * @param content true to get the height minus borders and padding
     * @return the element's height
     */
    public final int getHeight(boolean content) {
        int h = getOffsetHeight();
        if (content) {
            h -= getFrameWidth(Side.TOP, Side.BOTTOM);
        }
        return Math.max(0, h);
    }

    /**
     * Returns the element's id.
     * 
     * @param autoGenId true to set an id if one does not exist
     * @return the id
     */
    public final String getId(boolean autoGenId) {
        String id = getId();
        if (!autoGenId || !(id == null || (id.length() == 0))) {
            return id;
        }

        id = DomIdProvider.generateId(this);
        setId(id);
        return id;
    }

    /**
     * Returns the top Y coordinate.
     * 
     * @return the top value
     */
    public final int getLeft() {
        return getLeft(true);
    }

    /**
     * Gets the left X coordinate.
     * 
     * @param local true to get the local css position instead of page coordinate
     * @return the left value
     */
    public final int getLeft(boolean local) {
        return local ? Util.parseInt(getStyle().getLeft(), 0) : getX();
    }

    /**
     * Returns the total margin size of the specified side.
     * 
     * @param side the side from which to calculate the margin.
     * @return the widths of the margin
     */
    public final int getMargins(Side side) {
        switch (side) {
        case LEFT:
            return Util.parseInt(getComputedStyle("marginLeft"), 0);
        case RIGHT:
            return Util.parseInt(getComputedStyle("marginRight"), 0);
        case TOP:
            return Util.parseInt(getComputedStyle("marginTop"), 0);
        case BOTTOM:
            return Util.parseInt(getComputedStyle("marginBottom"), 0);
        }
        assert false : "Unparsable Side " + side;
        return 0;
    }

    /**
     * Returns the total margin size of the specified sides.
     * 
     * @param sides the sides from which to calculate the margin. Passing more than one side will yield the sum of the
     *          margins of those sides of the element.
     * @return the sum of the widths of the margins
     */
    public final int getMargins(Side... sides) {
        List<String> list = new ArrayList<String>();
        for (Side side : sides) {
            appendSideToList(list, side, "margin");
        }

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the total margin size of the specified sides.
     * <p/>
     * Passing more than one side will yield the sum of the margins of those sides of the element.
     * 
     * @param side1 the first side from which to calculate the margin.
     * @param side2 the second side from which to calculate the margin.
     * @return the sum of the widths of the margins
     */
    public final int getMargins(Side side1, Side side2) {
        List<String> list = new ArrayList<String>();

        appendSideToList(list, side1, "margin");
        appendSideToList(list, side2, "margin");

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the offsets between two elements. Both element must be part of the DOM tree and not have display:none to
     * have page coordinates.
     * 
     * @param to the to element
     * @return the xy page offsets
     */
    public final Point getOffsetsTo(Element to) {
        Point o = getXY();

        XElement xto = (XElement) to;
        Point e = xto.getXY();
        return new Point(o.getX() - e.getX(), o.getY() - e.getY());
    }

    /**
     * Returns the width of the padding(s) for the specified side.
     * 
     * @param side the side from which to calculate the padding.
     * @return the sum of the widths of the padding
     */
    public final int getPadding(Side side) {
        switch (side) {
        case LEFT:
            return Util.parseInt(getComputedStyle("paddingLeft"), 0);
        case RIGHT:
            return Util.parseInt(getComputedStyle("paddingRight"), 0);
        case TOP:
            return Util.parseInt(getComputedStyle("paddingTop"), 0);
        case BOTTOM:
            return Util.parseInt(getComputedStyle("paddingBottom"), 0);
        }
        assert false : "Unparsable Side " + side;
        return 0;
    }

    /**
     * Returns the width of the padding(s) for the specified side(s).
     * 
     * @param sides the sides from which to calculate the padding. Passing more than one side will yield the sum of the
     *          padding of those sides of the element.
     * @return the sum of the widths of the padding
     */
    public final int getPadding(Side... sides) {
        List<String> list = new ArrayList<String>();
        for (Side side : sides) {
            appendSideToList(list, side, "padding");
        }

        return getStylePropertyTotal(list);
    }

    /**
     * Returns the widget's current position. The widget must be attached to return page coordinates.
     * 
     * @param local true to return the element's left and top rather than page coordinates
     * @return the position
     */
    public final Point getPosition(boolean local) {
        if (local) {
            return new Point(getLeft(true), getTop(true));
        }
        return getXY();
    }

    /**
     * Returns the region of the given element. The element must be part of the DOM tree to have a region.
     * 
     * @return a region containing top, left, bottom, right
     */
    public final Region getRegion() {
        Rectangle bounds = getBounds();
        Region r = new Region();
        r.setLeft(bounds.getX());
        r.setTop(bounds.getY());
        r.setRight(r.getLeft() + bounds.getWidth());
        r.setBottom(r.getTop() + bounds.getHeight());
        return r;
    }

    /**
     * Returns the right X coordinate of the element (element X position + element width).
     * 
     * @param local <code>true</code> to get the local css position instead of page coordinate
     * @return the right value
     */
    public final int getRight(boolean local) {
        return getOffsetWidth() + (local ? getLeft(true) : getX());
    }

    /**
     * Returns the body elements current scroll position.
     * 
     * @return the scroll position
     */
    public final Scroll getScroll() {
        if (this.cast() == Document.get().getBody() || this == Document.get().getDocumentElement()) {
            return new Scroll(XDOM.getBodyScrollLeft(), XDOM.getBodyScrollTop());
        } else {
            return new Scroll(getScrollLeft(), getScrollTop());
        }
    }

    /**
     * Returns the size of the element.
     * 
     * @return the size
     */
    public final Size getSize() {
        return getSize(false);
    }

    /**
     * Returns the element's size.
     * 
     * @param content true to get the size minus borders and padding
     * @return the size
     */
    public final Size getSize(boolean content) {
        int w = getOffsetWidth();
        int h = getOffsetHeight();
        if (content) {
            Size frameWidth = getFrameSize();
            w -= frameWidth.getWidth();
            h -= frameWidth.getHeight();
        }
        return new Size(Math.max(0, w), Math.max(0, h));
    }

    /**
     * Returns the element's size (excluding padding and borders), using style attribute before offsets.
     * 
     * @return the size
     */
    public final Size getStyleSize() {
        return getStyleSize(true);
    }

    /**
     * Returns the element's size, using style attribute before offsets.
     * 
     * @param contentOnly true to exclude padding and borders
     * @return the size
     */
    public final Size getStyleSize(boolean contentOnly) {
        int h = Util.parseInt(getStyle().getProperty("height"), Style.DEFAULT);
        int w = Util.parseInt(getStyle().getProperty("width"), Style.DEFAULT);

        boolean isBorderBox = isBorderBox();

        if (isBorderBox && contentOnly && w != Style.DEFAULT) {
            w -= getFrameWidth(Side.LEFT, Side.RIGHT);
            if (w < 0) {
                w = Style.DEFAULT;
            }
        } else if (!isBorderBox && !contentOnly && w != Style.DEFAULT) {
            w += getFrameWidth(Side.LEFT, Side.RIGHT);
        }
        if (isBorderBox && contentOnly && h != Style.DEFAULT) {
            h -= getFrameWidth(Side.TOP, Side.BOTTOM);
            if (h < 0) {
                h = Style.DEFAULT;
            }
        } else if (!isBorderBox && !contentOnly && h != Style.DEFAULT) {
            h += getFrameWidth(Side.TOP, Side.BOTTOM);
        }

        int offsetWidth = Style.DEFAULT;
        int offsetHeight = Style.DEFAULT;
        if (w == Style.DEFAULT && h == Style.DEFAULT) {
            Size s = getSize(contentOnly);
            offsetWidth = s.getWidth();
            offsetHeight = s.getHeight();
            if (s.getWidth() > 0) {
                w = s.getWidth();
            }
            if (s.getHeight() > 0) {
                h = s.getHeight();
            }
        } else if (w == Style.DEFAULT) {
            offsetWidth = getWidth(contentOnly);
            if (offsetWidth > 0) {
                w = offsetWidth;
            }
        } else if (h == Style.DEFAULT) {
            offsetHeight = getHeight(contentOnly);
            if (offsetHeight > 0) {
                h = offsetHeight;
            }
        }

        List<String> l = new ArrayList<String>();
        if (w == Style.DEFAULT) {
            l.add("width");
        }
        if (h == Style.DEFAULT) {
            l.add("height");
        }
        Map<String, String> map = getComputedStyle(l);
        if (map != null) {
            String wid = map.get("width");
            if (wid != null) {
                w = Util.parseInt(wid, Style.DEFAULT);
                if (offsetWidth == 0 && isBorderBox && contentOnly && w != Style.DEFAULT && !GXT.isIE()) {
                    w -= getFrameWidth(Side.LEFT, Side.RIGHT);
                } else if (GXT.isIE() && isBorderBox && w != Style.DEFAULT && contentOnly) {
                    w -= getFrameWidth(Side.LEFT, Side.RIGHT);
                } else if (offsetWidth == 0 && !isBorderBox && !contentOnly && w != Style.DEFAULT) {
                    w += getFrameWidth(Side.LEFT, Side.RIGHT);
                }
            }
            String hei = map.get("height");
            if (hei != null) {
                h = Util.parseInt(hei, Style.DEFAULT);
                if (offsetHeight == 0 && isBorderBox && contentOnly && h != Style.DEFAULT && !GXT.isIE()) {
                    h -= getFrameWidth(Side.TOP, Side.BOTTOM);
                } else if (GXT.isIE() && isBorderBox && h != Style.DEFAULT && contentOnly) {
                    h -= getFrameWidth(Side.TOP, Side.BOTTOM);
                } else if (offsetHeight == 0 && !isBorderBox && !contentOnly && h != Style.DEFAULT) {
                    h += getFrameWidth(Side.TOP, Side.BOTTOM);
                }
            }
        }
        if (w == Style.DEFAULT && h == Style.DEFAULT) {
            return new Size(offsetWidth, offsetHeight);
        }
        return new Size(w != Style.DEFAULT ? w : offsetWidth, h != Style.DEFAULT ? h : offsetHeight);
    }

    /**
     * Returns the element's sub child.
     * 
     * @param depth the child node depth
     * @return the child element
     */
    public final Element getSubChild(int depth) {
        Element child = this;
        while (depth-- > 0) {
            child = child.getChild(0).cast();
        }
        return child;
    }

    /**
     * Returns the measured width of the element's text.
     * 
     * @return the width
     */
    public final int getTextWidth() {
        String text = getInnerText();
        TextMetrics metrics = TextMetrics.get();
        metrics.bind(this);
        return metrics.getWidth(text);
    }

    /**
     * Returns the top Y coordinate.
     * 
     * @return the top value
     */
    public final int getTop() {
        return getTop(true);
    }

    /**
     * Gets the top Y coordinate.
     * 
     * @param local true to get the local css position instead of page coordinate
     * @return the top value
     */
    public final int getTop(boolean local) {
        return local ? Util.parseInt(getStyle().getTop(), 0) : getY();
    }

    /**
     * Returns the vis mode.
     * 
     * @return the vis mode
     */
    public final native VisMode getVisMode() /*-{
                                             return this.visMode;
                                             }-*/;

    /**
     * Returns the element's width.
     * 
     * @param content true to get the width minus borders and padding
     * @return the width
     */
    public final int getWidth(boolean content) {
        int w = getOffsetWidth();
        if (content) {
            w -= getFrameWidth(Side.LEFT, Side.RIGHT);
        }
        return Math.max(0, w);
    }

    /**
     * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have
     * page coordinates.
     * 
     * @return the x position of the element
     */
    public final int getX() {
        int x = getAbsoluteLeft();
        return x > 0 ? x - FieldHolder.adjustXYOffset : x;
    }

    /**
     * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have
     * page coordinates.
     * 
     * @return the location
     */
    public final Point getXY() {
        return new Point(getX(), getY());
    }

    /**
     * Gets the current Y position of the element based on page coordinates.
     * 
     * @return the y position of the element
     */
    public final int getY() {
        int y = getAbsoluteTop();
        return y > 0 ? y - FieldHolder.adjustXYOffset : y;
    }

    /**
     * Returns the element's z-index.
     * 
     * @return the z-index
     */
    public final int getZIndex() {
        try {
            return Util.parseInt(getStyle().getZIndex(), 0);
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * Hides this element.
     */
    public final void hide() {
        setVisible(false);
    }

    /**
     * Returns the index of the child.
     * 
     * @param child the child
     * @return the index or -1 of not a child
     */
    public final int indexOf(Element child) {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            if (getChild(i) == child) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Inserts this element before the passed element.
     * 
     * @param element the element to insert before
     */
    public final void insertBefore(Element element) {
        element.getParentElement().insertBefore(this, element);
    }

    /**
     * Inserts the elements before this element.
     * 
     * @param elements the elements to insert
     */
    public final void insertBefore(NodeList<Element> elements) {
        Element parent = getParentElement();
        assert parent != null : "cannot insertBefore with null parent";
        for (int i = 0, len = elements.getLength(); i < len; i++) {
            parent.insertBefore(elements.getItem(0), this);
        }
    }

    /**
     * Inserts an element at the specified index.
     * 
     * @param child the child element
     * @param index the insert location
     */
    public final void insertChild(Element child, int index) {
        DOM.insertChild((com.google.gwt.dom.client.Element) this.cast(), child, index);
    }

    /**
     * Creates and inserts a child element.
     * 
     * @param html the HTML fragment
     * @return the new child
     */
    public final XElement insertFirst(SafeHtml html) {
        return XElement.as(DomHelper.insertFirst(this, html));
    }

    /**
     * Inserts an html fragment into this element
     * 
     * @param where where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
     * @param html the HTML fragment
     * @return the inserted node (or nearest related if more than 1 inserted)
     */
    public final XElement insertHtml(String where, SafeHtml html) {
        return XElement.as(DomHelper.insertHtml(where, this, html));
    }

    /**
     * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child).
     * 
     * @param selector selector
     * @return true if the element matches the selector, else false
     */
    public final boolean is(String selector) {
        return DomQuery.is(this, selector);
    }

    /**
     * Returns true if the element is a border box.
     * 
     * @return true for border box
     */
    public final boolean isBorderBox() {
        // only supported mode is w3c model as we force strict
        return false;
        // return isBorderBox(this);
    }

    /**
     * Returns true if the element is part of the browser's DOM.
     * 
     * @return the connected state
     */
    public final boolean isConnected() {
        return Document.get().getBody().isOrHasChild(this);
    }

    /**
     * Determine whether an element is equal to, or the child of, this element.<br/>
     * <br/>
     * This implementation delegates to <code>Node.isOrHasChildImpl(Node)</code>, but catches a scenario
     * in IE9 & IE10 where not all elements support the contains() method used by the deferred binding to
     * <code>DOMImplTrident.isOrHasChildImpl(Node, Node)</code>.
     *
     * @param child the potential child element
     * @return <code>true</code> if the relationship holds
     * @see com.google.gwt.dom.client.Node#isOrHasChild(Node)
     * @see com.google.gwt.dom.client.DOMImplTrident#isOrHasChildImpl(Node, Node)
     */
    public final boolean isOrHasChild(XElement child) {
        try {
            return super.isOrHasChild(child);
        } catch (JavaScriptException e) {
            // swallow the exception if it is a manifestation of the IE9 & IE10 bug
            if ((GXT.isIE9() || GXT.isIE10()) && e.getName().equals("TypeError")
                    && e.getMessage().contains("'contains'")) {
                return false;
            } else {
                throw e;
            }
        }
    }

    /**
     * Returns whether the element is scrollable (x or y).
     * 
     * @return true if scrollable
     */
    public final boolean isScrollable() {
        return isScrollableX() || isScrollableY();
    }

    /**
     * Returns whether the element is scrollable on the x-axis.
     * 
     * @return true if scrollable on the x-axis
     */
    public final boolean isScrollableX() {
        return getScrollWidth() > getClientWidth();
    }

    /**
     * Returns whether the element is scrollable on the y-axis.
     * 
     * @return true if scrollable on the y-axis
     */
    public final boolean isScrollableY() {
        return getScrollHeight() > getClientHeight();
    }

    /**
     * Tests the style for the given value.
     * 
     * @param attr the style name
     * @param value the test value
     * @return true if equal
     */
    public final boolean isStyleAttribute(String attr, String value) {
        String a = getComputedStyle(attr);
        return a != null && a.equals(value);
    }

    /**
     * Tests style property values for matches.
     * 
     * @param map the map of style property names and values
     * @param matchAll true to match all properties
     * @return true if a match is found, otherwise false
     */
    public final boolean isStyleProperty(Map<String, String> map, boolean matchAll) {
        Set<String> collection = map.keySet();
        FastMap<String> a = getComputedStyle(new ArrayList<String>(collection));
        for (String s : collection) {
            if (map.get(s).equals(a.get(s))) {
                if (!matchAll) {
                    return true;
                }
            } else {
                if (matchAll) {
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * Returns whether the element is currently visible.
     * 
     * @return true if visible
     */
    public final boolean isVisible() {
        return isVisible(false);
    }

    /**
     * Returns whether the element is currently visible.
     * 
     * @param deep true to deep test
     * 
     * @return true if visible
     */
    public final boolean isVisible(boolean deep) {
        Map<String, String> map = new FastMap<String>();
        map.put("visibility", "hidden");
        map.put("display", "none");
        boolean vis = !isStyleProperty(map, false);
        XElement parent = (XElement) getParentElement();
        XElement p = parent != null ? parent : null;
        if (p == null) {
            return false;
        }
        if (!deep || !vis) {
            return vis;
        }

        while (p != null && p != Document.get().getDocumentElement()) {
            if (!p.isVisible()) {
                return false;
            }
            p = (XElement) p.getParentElement();
        }
        return true;
    }

    /**
     * Retrieves the data using the request builder and updates the element's contents.
     * <p/>
     * Please note that the <code>Response</code> from the <code>RequestBuilder</code> is treated as raw html
     * without any sanitizing. If is up to the caller to ensure that the call does not return unsafe html.
     * <p/>
     * This method is subject to change.
     * 
     * @param builder the request builder
     */
    public final Request load(RequestBuilder builder) {
        try {
            builder.setCallback(new RequestCallback() {

                public void onError(Request request, Throwable exception) {
                    setInnerSafeHtml(exception != null && exception.getMessage() != null
                            ? SafeHtmlUtils.fromString(exception.getMessage())
                            : SafeHtmlUtils.EMPTY_SAFE_HTML);
                }

                public void onResponseReceived(Request request, Response response) {
                    setInnerSafeHtml(response != null && response.getText() != null
                            ? SafeHtmlUtils.fromString(response.getText())
                            : SafeHtmlUtils.EMPTY_SAFE_HTML);
                }

            });
            return builder.send();
        } catch (Exception e) {
            setInnerSafeHtml(e != null && e.getMessage() != null ? SafeHtmlUtils.fromString(e.getMessage())
                    : SafeHtmlUtils.EMPTY_SAFE_HTML);
            return null;
        }
    }

    /**
     * Makes an element positionable.
     */
    public final void makePositionable() {
        makePositionable(false);
    }

    /**
     * Makes an element positionable.
     * 
     * @param absolute <code>true</code> to position absolutely
     */
    public final void makePositionable(boolean absolute) {
        if (absolute) {
            getStyle().setPosition(Position.ABSOLUTE);
        } else {
            String p = getComputedStyle("position");
            if (p == null || "".equals(p) || "static".equals(p) && (!"absolute".equals(p))) {
                getStyle().setPosition(Position.RELATIVE);
            }
        }
    }

    /**
     * Puts a mask over this element to disable user interaction.
     * 
     * @param message a message to display in the mask
     */
    public final void mask(String message) {
        Mask.mask(this, message);
    }

    /**
     * Removes all the elements children.
     */
    public final void removeChildren() {
        Element child = null;
        while ((child = getFirstChildElement()) != null) {
            removeChild(child);
        }
        String tag = getTagName().toLowerCase();
        if (!tag.equals("table") && !tag.equals("tbody") && !tag.equals("tr") && !tag.equals("td")) {
            setInnerSafeHtml(SafeHtmlUtils.EMPTY_SAFE_HTML);
        }
    }

    /**
     * Removes the style names(s) from the element.
     * 
     * @param classNames the style names
     */
    public final void removeClassName(String... classNames) {
        for (int i = 0; i < classNames.length; i++) {
            removeClassName(classNames[i]);
        }
    }

    /**
     * Removes the style names(s) from the element.
     * 
     * @param className1 the first class name to remove
     * @param className2 the second class name to remove
     */
    public final void removeClassName(String className1, String className2) {
        removeClassName(className1);
        removeClassName(className2);
    }

    /**
     * Forces the Browser to repaint this element.
     */
    public final void repaint() {
        addClassName(CommonStyles.get().repaint());
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {

            @Override
            public void execute() {
                removeClassName(CommonStyles.get().repaint());
            }
        });
    }

    /**
     * Replace one class name with another.
     * 
     * @param oldClassName the class name to be replaced
     * @param newClassName the class name to replace it
     * @param deep true to class names on child elements
     */
    public final void replaceClassName(String oldClassName, String newClassName, boolean deep) {
        removeClassName(oldClassName);
        addClassName(newClassName);
        NodeList<Element> nodes = select("." + oldClassName);
        for (int i = 0; i < nodes.getLength(); i++) {
            nodes.getItem(i).replaceClassName(oldClassName, newClassName);
        }
    }

    /**
     * Restores any elements made visible with {@link #ensureVisible()}.
     * 
     * @param list the meta data list
     */
    public final void restoreVisible(List<FastMap<Object>> list) {
        if (list != null) {
            for (FastMap<Object> m : list) {
                Element e = (Element) m.get("element");

                Boolean offset = (Boolean) m.get("hasxhideoffset");
                if (offset != null) {
                    if (!offset.booleanValue()) {
                        e.removeClassName(CommonStyles.get().hideOffsets());
                    }
                }

                Boolean display = (Boolean) m.get("hasxhidedisplay");
                if (display != null) {
                    if (display.booleanValue()) {
                        e.addClassName(CommonStyles.get().hideDisplay());
                    }
                }

                e.getStyle().setProperty("display", (String) m.get("origd"));
            }
        }
    }

    /**
     * Scrolls the element into view.
     * 
     * @param container the container element
     * @param hscroll <code>false</code> to disable horizontal scrolling.
     */
    public final void scrollIntoView(Element container, boolean hscroll) {
        scrollIntoView(container, hscroll, 0, 0);
    }

    /**
     * Scrolls the element into view.
     * 
     * @param container the container element
     * @param hscroll <code>false</code> to disable horizontal scrolling.
     * @param offsetX X offset
     * @param offsetY Y offset
     */
    public final void scrollIntoView(Element container, boolean hscroll, int offsetX, int offsetY) {
        Element c = container != null ? container : Document.get().getBody();

        Point o = getOffsetsTo(c);
        int l = o.getX();
        int t = o.getY();
        l = l + c.getScrollLeft();
        t = t + c.getScrollTop();
        int b = t + getOffsetHeight() + offsetY;
        int r = l + getOffsetWidth() + offsetX;

        int ch = c.getClientHeight();
        int ct = c.getScrollTop();
        int cb = ct + ch;

        if (getOffsetHeight() > ch || t < ct) {
            c.setScrollTop(t);
        } else if (b > cb) {
            c.setScrollTop(b - ch);
        }

        if (hscroll) {
            int cl = c.getScrollLeft();
            int cw = c.getClientWidth();
            int cr = cl + cw;

            if (getOffsetWidth() > cw || l < cl) {
                c.setScrollLeft(l);
            } else if (r > cr) {
                c.setScrollLeft(r - cw);
            }
        }
    }

    /**
     * Scrolls this element the specified scroll point.
     * 
     * @param side the scroll direction
     * @param value the new scroll value
     */
    public final void scrollTo(ScrollDirection side, int value) {
        if (side == ScrollDirection.LEFT) {
            setScrollLeft(value);
        } else {
            setScrollTop(value);
        }
    }

    /**
     * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
     * 
     * @param selector the selector/xpath query
     * @return the matching elements
     */
    public final NodeList<Element> select(String selector) {
        return DomQuery.select(selector, this);
    }

    /**
     * Selects a single element.
     * 
     * @param selector the CSS selector
     * @return the matching element or null if no match
     */
    public final XElement selectNode(String selector) {
        Element el = DomQuery.selectNode(selector, this);
        if (el != null) {
            return XElement.as(el);
        }
        return null;
    }

    /**
     * Sets the attribute, determined by it names, using the given name space and value.
     * 
     * @param nameSpace the name space of the attribute
     * @param name the attribute name
     * @param value the value of the attribute
     */
    public final native void setAttributeNS(String nameSpace, String name, String value)/*-{
                                                                                        this.setAttributeNS(nameSpace, name, value);
                                                                                        }-*/;

    /**
     * Adds or removes a border.
     * 
     * @param show the show state
     */
    public final void setBorders(boolean show) {
        if (show) {
            addClassName(ThemeStyles.get().style().border());
            getStyle().setBorderWidth(1, Unit.PX);
        } else {
            removeClassName(ThemeStyles.get().style().border());
            getStyle().setBorderWidth(0, Unit.PX);
        }
    }

    /**
     * Sets the element's bounds.
     * 
     * @param x the x coordinate
     * @param y the y coordinate
     * @param width the new width
     * @param height the new height
     */
    public final void setBounds(int x, int y, int width, int height) {
        setBounds(x, y, width, height, false);
    }

    /**
     * Sets the element's bounds.
     * 
     * @param x the x coordinate
     * @param y the y coordinate
     * @param width the new width
     * @param height the new height
     * @param adjust true to adjust for box model issues
     */
    public final void setBounds(int x, int y, int width, int height, boolean adjust) {
        setXY(x, y);
        setSize(width, height, adjust);
    }

    /**
     * Sets the element's bounds.
     * 
     * @param bounds the new bounds
     */
    public final void setBounds(Rectangle bounds) {
        setBounds(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
    }

    /**
     * Sets the element's bounds.
     * 
     * @param bounds the new bounds
     * @param content <code>true</code> to adjust for box model issues
     */
    public final void setBounds(Rectangle bounds, boolean content) {
        setBounds(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), content);
    }

    /**
     * Adds or removes the class name.
     * 
     * @param cls the style name
     * @param add true to add, false to remove
     */
    public final void setClassName(String cls, boolean add) {
        if (add) {
            addClassName(cls);
        } else {
            removeClassName(cls);
        }
    }

    /**
     * Sets the CSS display property.
     * 
     * @param display true to display the element using its default display
     */
    public final void setDisplayed(boolean display) {
        getStyle().setDisplay(display ? Display.BLOCK : Display.NONE);
        if (getLayer() != null) {
            if (display) {
                getLayer().sync(true);
            } else {
                getLayer().hideUnders();
            }
        }
    }

    /**
     * Sets the elements height.
     * 
     * @param height the height
     */
    public final void setHeight(int height) {
        setHeight(height, false);
    }

    /**
     * Sets the elements height.
     * 
     * @param height the height
     * @param adjust <code>true</code> to adjust for box model issues
     */
    public final void setHeight(int height, boolean adjust) {
        if (adjust && !isBorderBox()) {
            height -= getFrameWidth(Side.TOP, Side.BOTTOM);
        }
        if (height >= 0) {
            getStyle().setPropertyPx("height", height);
        }
    }

    /**
     * Sets the elements height.
     * 
     * @param height the height
     */
    public final void setHeight(String height) {
        getStyle().setProperty("height", addUnits(height, "px"));
    }

    /**
     * Sets the element's left position directly using CSS style (instead of {@link #setX}).
     * 
     * @param left the left value in pixels
     */
    public final void setLeft(int left) {
        getStyle().setLeft(left, Unit.PX);
        if (getLayer() != null) {
            getLayer().sync(false);
        }
    }

    /**
     * Quick set left and top adding default units.
     * 
     * @param left the left value
     * @param top the top value
     */
    public final void setLeftTop(int left, int top) {
        setLeft(left);
        setTop(top);
    }

    /**
     * Sets the elements's margin.
     * 
     * @param margin the margin
     */
    public final void setMargins(int margin) {
        setMargins(new Margins(margin));
    }

    /**
     * Sets the elements's margin.
     * 
     * @param margin the margin
     */
    public final void setMargins(Margins margin) {
        if (margin != null) {
            getStyle().setMarginLeft(margin.getLeft(), Unit.PX);
            getStyle().setMarginTop(margin.getTop(), Unit.PX);
            getStyle().setMarginRight(margin.getRight(), Unit.PX);
            getStyle().setMarginBottom(margin.getBottom(), Unit.PX);
        }
    }

    /**
     * Cross-browser support for setting opacity.
     * 
     * @param opacity the opacity
     */
    public final void setOpacity(double opacity) {
        FieldHolder.computedStyle.setStyleAttribute(this, "opacity", opacity);
    }

    /**
     * Sets the elements's padding.
     * 
     * @param padding the padding
     */
    public final void setPadding(Padding padding) {
        if (padding != null) {
            getStyle().setPaddingLeft(padding.getLeft(), Unit.PX);
            getStyle().setPaddingTop(padding.getTop(), Unit.PX);
            getStyle().setPaddingRight(padding.getRight(), Unit.PX);
            getStyle().setPaddingBottom(padding.getBottom(), Unit.PX);
        }
    }

    /**
     * Sets the element's size.
     * 
     * @param width the new width
     * @param height the new height
     */
    public final void setSize(int width, int height) {
        setSize(width, height, false);
    }

    /**
     * Set the size of the element.
     * 
     * @param width the new width
     * @param height the new height
     * @param adjust <code>true</code> to adjust for box model issues
     */
    public final void setSize(int width, int height, boolean adjust) {
        if (adjust && !isBorderBox()) {
            Size frameWidth = getFrameSize();
            width -= frameWidth.getWidth();
            height -= frameWidth.getHeight();
        }
        if (width >= 0) {
            getStyle().setPropertyPx("width", width);
        }
        if (height >= 0) {
            getStyle().setPropertyPx("height", height);
        }
        if (getLayer() != null) {
            getLayer().sync(false);
        }
    }

    /**
     * Sets the element's size.
     * 
     * @param size the size
     */
    public final void setSize(Size size) {
        setSize(size.getWidth(), size.getHeight());
    }

    /**
     * Sets the element's top position directly using CSS style (instead of {@link #setY}).
     * 
     * @param top the top value in pixels
     */
    public final void setTop(int top) {
        getStyle().setTop(top, Unit.PX);
        if (getLayer() != null) {
            getLayer().sync(false);
        }
    }

    /**
     * Sets the elements CSS visibility property.
     * 
     * @param visible true for visible
     */
    public final void setVisibility(boolean visible) {
        getStyle().setVisibility(visible ? Visibility.VISIBLE : Visibility.HIDDEN);
    }

    /**
     * Sets the visibility of the element using the {@link #getVisMode()}.
     * 
     * @param visible whether the element is visible
     */
    public final void setVisible(boolean visible) {
        if (getVisMode() == null || getVisMode() == VisMode.DISPLAY) {
            setDisplayed(visible);
        } else {
            setVisibility(visible);
        }
    }

    /**
     * Sets the vis mode (defaults to {@link VisMode#DISPLAY}.
     * 
     * @param visMode the vis mode
     */
    public final native void setVisMode(VisMode visMode) /*-{
                                                         this.visMode = visMode;
                                                         }-*/;

    /**
     * Sets the element's width.
     * 
     * @param width the new width
     */
    public final void setWidth(int width) {
        setWidth(width, false);
    }

    /**
     * Sets the elements's width.
     * 
     * @param width the new width
     * @param adjust <code>true</code> to adjust for box model issues
     */
    public final void setWidth(int width, boolean adjust) {
        if (adjust && !isBorderBox()) {
            width -= getFrameWidth(Side.LEFT, Side.RIGHT);
        }
        if (width >= 0) {
            getStyle().setPropertyPx("width", width);
        }
    }

    /**
     * Sets the elements width.
     * 
     * @param width the width
     */
    public final void setWidth(String width) {
        getStyle().setProperty("width", addUnits(width, "px"));
    }

    /**
     * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page
     * coordinates.
     * 
     * @param x the x coordinate
     */
    public final void setX(int x) {
        setXY(x, Style.DEFAULT);
    }

    /**
     * Sets the elements position in page coordinates.
     * 
     * @param x the x coordinate
     * @param y the y coordinate
     */
    public final void setXY(int x, int y) {
        setXY(new Point(x, y));
    }

    /**
     * Sets the element's position in page coordinates.
     * 
     * @param p the position
     */
    public final void setXY(Point p) {
        makePositionable();
        Point pts = translatePoints(p);
        if (p.getX() != Style.DEFAULT) {
            setLeft(pts.getX());
        }
        if (p.getY() != Style.DEFAULT) {
            setTop(pts.getY());
        }
    }

    /**
     * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page
     * coordinates.
     * 
     * @param y the y coordinate
     */
    public final void setY(int y) {
        setXY(Style.DEFAULT, y);
    }

    /**
     * Sets the element's z-index.
     * 
     * @param zIndex the z-index value
     */
    public final void setZIndex(int zIndex) {
        DOM.setIntStyleAttribute(this, "zIndex", Math.max(0, zIndex));
    }

    /**
     * Shows this element.
     */
    public final void show() {
        setVisible(true);
    }

    /**
     * Replace one class name with another.
     * 
     * @param oldClassName the class name to be replaced, empty strings allowed
     * @param newClassName the class name to replace it, empty strings allowed
     */
    public final void swapClassName(String oldClassName, String newClassName) {
        if (!oldClassName.equals("")) {
            removeClassName(oldClassName);
        }
        if (!newClassName.equals("")) {
            addClassName(newClassName);
        }
    }

    /**
     * Translates the passed page coordinates into left/top css values for this element.
     * 
     * @param p the coordinates
     * @return the translated coordinates
     */
    public final Point translatePoints(Point p) {
        List<String> list = new ArrayList<String>(3);
        list.add("position");
        list.add("left");
        list.add("top");

        Map<String, String> map = getComputedStyle(list);
        boolean relative = "relative".equals(map.get("position"));
        int l = Util.parseInt(map.get("left"), -11234);
        int t = Util.parseInt(map.get("top"), -11234);

        l = l != -11234 ? l : (relative ? 0 : getOffsetLeft());
        t = t != -11234 ? t : (relative ? 0 : getOffsetTop());

        Point o = getXY();
        return new Point(p.getX() - o.getX() + l, p.getY() - o.getY() + t);
    }

    /**
     * Removes a previously applied mask.
     */
    public final void unmask() {
        Mask.unmask(this);
    }

    /**
     * Unwraps the child element.
     * 
     * @param bounds the original bounds
     */
    public final void unwrap(XElement child, Rectangle bounds) {
        child.setLeftTop(bounds.getX(), bounds.getY());
        XElement p = getParentElement().cast();
        int pos = p.getChildIndex(this);
        p.insertChild(child, pos);
        p.removeChild(this);
    }

    /**
     * Sets the element's z-index using {@link XDOM#getTopZIndex()} to ensure it has the highest values.
     * 
     * @param adj the adjustment to be applied to the z-index value
     */
    public final void updateZIndex(int adj) {
        getStyle().setZIndex(XDOM.getTopZIndex() + adj);
    }

    /**
     * Wraps the element with the specified wrapper. The wrapper will have the same size and position of the element. The
     * original bounds can be used to 'unwrap' the element.
     * 
     * @param wrapper the wrapper element
     * @return the original bounds
     */
    public final Rectangle wrap(Element wrapper) {
        XElement wrap = XElement.as(wrapper);
        wrap.setVisible(false);

        String pos = getStyle().getPosition();
        wrap.getStyle().setProperty("position", pos);

        int l = getLeft();
        int t = getTop();

        setLeft(5000);
        setVisible(true);

        int h = getComputedHeight();
        int w = getComputedWidth();

        setLeft(1);
        getStyle().setOverflow(Overflow.HIDDEN);
        setVisible(false);

        wrap.insertBefore(this);
        wrap.appendChild(this);

        wrap.getStyle().setOverflow(Overflow.HIDDEN);

        wrap.setLeft(l);
        wrap.setTop(t);

        setTop(0);
        setLeft(0);

        return new Rectangle(l, t, w, h);
    }

    protected final Point getConstrainToXY(Element elem, Point proposedXY) {
        int vw, vh, vx = 0, vy = 0;
        if (elem == Document.get().getBody()) {
            vw = XDOM.getViewportSize().getWidth();
            vh = XDOM.getViewportSize().getHeight();
        } else {
            vw = elem.getOffsetWidth();
            vh = elem.getOffsetHeight();
            vx = elem.getAbsoluteLeft();
            vy = elem.getAbsoluteTop();
        }

        Point xy = proposedXY;
        int x = xy.getX();
        int y = xy.getY();

        int vr = vx + vw;
        int vb = vy + vh;

        int w = getOffsetWidth();
        int h = getOffsetHeight();

        if ((x + w) > vr) {
            x = vr - w;
        }
        if ((y + h) > vb) {
            y = vb - h;

        }

        // then make sure top/left isn't negative
        if (x < vx) {
            x = vx;
        }
        if (y < vy) {
            y = vy;
        }

        return new Point(x, y);
    }

    final native Layer getLayer() /*-{
                                  return this.layer;
                                  }-*/;

    final native void setLayer(Layer layer) /*-{
                                            this.layer = layer;
                                            }-*/;

    private void appendSideToList(List<String> list, Side side, String prop) {
        String s = side.toString();
        if (prop != null) {
            String temp = prop + s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
            if (prop.equals("border")) {
                temp += "Width";
            }
            list.add(temp);
        }
    }

    private int getStylePropertyTotal(List<String> props) {
        int total = 0;
        FastMap<String> map = getComputedStyle(props);
        for (String s : map.keySet()) {
            total += Util.parseInt(map.get(s), 0);
        }
        return total;
    }

    private final boolean isLeftOrRight(String s) {
        return FieldHolder.leftOrRight.test(s);
    }

}