Package com.sencha.gxt.core.client.dom

Source Code of com.sencha.gxt.core.client.dom.XElement$FieldHolder

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
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.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.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 element is within the browser viewport.
   *
   * @param p the target destination
   * @return the new location
   */
  public final Point adjustForConstraints(Point p) {
    return getConstrainToXY(Document.get().getBody(), p);
  }

  /**
   * 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(String 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. 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);
  }

  /**
   * 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();
      // 5px of margin for ie
      int dw = XDOM.getViewWidth(false) - 10;
      int dh = XDOM.getViewHeight(false) - 10;

      // 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 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) {
    int frameWidth = 0;

    List<String> list = new ArrayList<String>();

    for (Side s : side) {
      appendFrameWidthStyles(list, s);
    }

    FastMap<String> map = getComputedStyle(list);
    for (String s : map.values()) {
      frameWidth += Util.parseInt(s, 0);
    }
    return frameWidth;
  }

  private void appendFrameWidthStyles(List<String> list, Side s) {
    switch (s) {
      case LEFT:
        list.add("paddingLeft");
        list.add("borderLeftWidth");
        break;

      case RIGHT:
        list.add("paddingRight");
        list.add("borderRightWidth");
        break;

      case TOP:
        list.add("paddingTop");
        list.add("borderTopWidth");
        break;
      case BOTTOM:
        list.add("paddingBottom");
        list.add("borderBottomWidth");
        break;
    }
  }

  public final int getFrameWidth(Side side) {
    List<String> list = new ArrayList<String>();

    appendFrameWidthStyles(list, side);
    int frameWidth = 0;
    FastMap<String> map = getComputedStyle(list);
    for (String s : map.values()) {
      frameWidth += Util.parseInt(s, 0);
    }
    return frameWidth;
  }

  public final int getFrameWidth(Side side1, Side side2) {
    List<String> list = new ArrayList<String>();

    appendFrameWidthStyles(list, side1);
    appendFrameWidthStyles(list, side2);
    int frameWidth = 0;
    FastMap<String> map = getComputedStyle(list);
    for (String s : map.values()) {
      frameWidth += Util.parseInt(s, 0);
    }
    return frameWidth;
  }

  /**
   * 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) {
    if (side == null) {
      return 0;
    }
    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.
   * <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) {
    int margin = 0;
    List<String> list = new ArrayList<String>();

    appendMarginSideToList(list, side1);
    appendMarginSideToList(list, side2);

    FastMap<String> map = getComputedStyle(list);
    for (String s : map.keySet()) {
      margin += Util.parseInt(map.get(s), 0);
    }
    return margin;
  }

  private void appendMarginSideToList(List<String> list, Side side) {
    switch (side) {
      case LEFT:
        list.add("marginLeft");
        break;
      case RIGHT:
        list.add("marginRight");
        break;
      case TOP:
        list.add("marginTop");
        break;
      case BOTTOM:
        list.add("marginBottom");
        break;
    }
  }

  /**
   * 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) {
    int margin = 0;
    List<String> list = new ArrayList<String>();
    for (Side side : sides) {
      appendMarginSideToList(list, side);
    }
    FastMap<String> map = getComputedStyle(list);
    for (String s : map.keySet()) {
      margin += Util.parseInt(map.get(s), 0);
    }
    return margin;
  }

  /**
   * 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) {
    if (side == null) {
      return 0;
    }
    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) {
    int padding = 0;
    List<String> list = new ArrayList<String>();
    for (Side side : sides) {
      switch (side) {
        case LEFT:
          list.add("paddingLeft");
          break;
        case RIGHT:
          list.add("paddingRight");
          break;
        case TOP:
          list.add("paddingTop");
          break;
        case BOTTOM:
          list.add("paddingBottom");
          break;
      }
    }
    FastMap<String> map = getComputedStyle(list);
    for (String s : map.keySet()) {
      padding += Util.parseInt(map.get(s), 0);
    }
    return padding;
  }

  /**
   * 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 html = getInnerHTML();
    TextMetrics metrics = TextMetrics.get();
    metrics.bind(this);
    return metrics.getWidth(html);
  }

  /**
   * 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() {
    // TODO IE6 is throwing exception
    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(String 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, String 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);
  }

  /**
   * 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>
   * 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) {
          setInnerHTML(exception.getMessage());
        }

        public void onResponseReceived(Request request, Response response) {
          setInnerHTML(response.getText());
        }

      });
      return builder.send();
    } catch (Exception e) {
      setInnerHTML(e.getMessage());
      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")) {
      setInnerHTML("");
    }
  }

  /**
   * 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();
    }

    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 final boolean isLeftOrRight(String s) {
    return FieldHolder.leftOrRight.test(s);
  }

}
TOP

Related Classes of com.sencha.gxt.core.client.dom.XElement$FieldHolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.