Package com.alkacon.geranium.client.util

Source Code of com.alkacon.geranium.client.util.DomUtil$AttributeValue

/*
* This library is part of Geranium -
* an open source UI library for GWT.
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)-
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package com.alkacon.geranium.client.util;

import com.alkacon.geranium.client.ui.css.I_LayoutBundle;
import com.alkacon.geranium.client.util.impl.DOMImpl;
import com.alkacon.geranium.client.util.impl.DocumentStyleImpl;

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

import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
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.event.dom.client.DomEvent;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Utility class to access the HTML DOM.<p>
*/
public final class DomUtil {

    /**
     * HTML tag attributes.<p>
     */
    public static enum Attribute {

        /** class. */
        clazz {

            /**
             * @see java.lang.Enum#toString()
             */
            @Override
            public String toString() {

                return "class";
            }
        },

        /** title. */
        title;
    }

    /**
     * Helper class to encapsulate an attribute/value pair.<p>
     */
    public static class AttributeValue {

        /** The attribute. */
        private Attribute m_attr;

        /** The attribute value. */
        private String m_value;

        /**
         * Constructor.<p>
         *
         * @param attr the attribute
         */
        public AttributeValue(Attribute attr) {

            this(attr, null);
        }

        /**
         * Constructor.<p>
         *
         * @param attr the attribute
         * @param value the value
         */
        public AttributeValue(Attribute attr, String value) {

            m_attr = attr;
            m_value = value;
        }

        /**
         * Returns the attribute.<p>
         *
         * @return the attribute
         */
        public Attribute getAttr() {

            return m_attr;
        }

        /**
         * Returns the value.<p>
         *
         * @return the value
         */
        public String getValue() {

            return m_value;
        }

        /**
         * Sets the value.<p>
         *
         * @param value the value to set
         */
        public void setValue(String value) {

            m_value = value;
        }

        /**
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {

            StringBuffer sb = new StringBuffer();
            sb.append(m_attr.toString());
            if (m_value != null) {
                sb.append("=\"").append(m_value).append("\"");
            }
            return sb.toString();
        }
    }

    /**
     * CSS Colors.<p>
     */
    public static enum Color {

        /** CSS Color. */
        red;
    }

    /**
     * HTML entities.<p>
     */
    public static enum Entity {

        /** non-breaking space. */
        hellip,

        /** non-breaking space. */
        nbsp;

        /**
         * Returns the HTML code for this entity.<p>
         *
         * @return the HTML code for this entity
         */
        public String html() {

            return "&" + super.name() + ";";
        }
    }

    /** Form methods. */
    public static enum Method {

        /** The get method. */
        get,
        /** The post method. */
        post;
    }

    /**
     * CSS Properties.<p>
     */
    public static enum Style {

        /** CSS Property. */
        backgroundColor,

        /** CSS Property. */
        backgroundImage,

        /** CSS property. */
        borderLeftWidth,

        /** CSS property. */
        borderRightWidth,

        /** CSS Property. */
        borderStyle,

        /** CSS Property. */
        display,

        /** CSS Property. */
        floatCss {

            /**
             * @see java.lang.Enum#toString()
             */
            @Override
            public String toString() {

                return "float";
            }
        },

        /** CSS Property. */
        fontFamily,

        /** CSS Property. */
        fontSize,

        /** CSS Property. */
        fontSizeAdjust,

        /** CSS Property. */
        fontStretch,

        /** CSS Property. */
        fontStyle,

        /** CSS Property. */
        fontVariant,

        /** CSS Property. */
        fontWeight,

        /** CSS Property. */
        height,

        /** CSS Property. */
        left,

        /** CSS Property. */
        letterSpacing,

        /** CSS Property. */
        lineHeight,

        /** CSS Property. */
        marginBottom,

        /** CSS Property. */
        marginTop,

        /** CSS Property. */
        maxHeight,

        /** CSS Property. */
        minHeight,

        /** CSS Property. */
        opacity,

        /** CSS Property. */
        padding,

        /** CSS Property. */
        position,

        /** CSS Property. */
        textAlign,

        /** CSS Property. */
        textDecoration,

        /** CSS Property. */
        textIndent,

        /** CSS Property. */
        textShadow,

        /** CSS Property. */
        textTransform,

        /** CSS Property. */
        top,

        /** CSS Property. */
        visibility,

        /** CSS Property. */
        whiteSpace,

        /** CSS Property. */
        width,

        /** CSS Property. */
        wordSpacing,

        /** CSS Property. */
        wordWrap,

        /** CSS Property. */
        zIndex;

    }

    /**
     * CSS Property values.<p>
     */
    public static enum StyleValue {

        /** CSS Property value. */
        absolute,

        /** CSS Property value. */
        auto,

        /** CSS Property value. */
        hidden,

        /** CSS Property value. */
        inherit,

        /** CSS Property value. */
        none,

        /** CSS Property value. */
        normal,

        /** CSS Property value. */
        nowrap,

        /** CSS Property value. */
        transparent;
    }

    /**
     * HTML Tags.<p>
     */
    public static enum Tag {

        /** HTML Tag. */
        a,

        /** HTML Tag. */
        ALL {

            /**
             * @see java.lang.Enum#toString()
             */
            @Override
            public String toString() {

                return "*";
            }
        },

        /** HTML Tag. */
        b,

        /** HTML Tag. */
        body,

        /** HTML Tag. */
        div,

        /** HTML Tag. */
        h1,

        /** HTML Tag. */
        h2,

        /** HTML Tag. */
        h3,

        /** HTML Tag. */
        h4,

        /** HTML-Tag. */
        iframe,

        /** HTML Tag. */
        li,

        /** HTML Tag. */
        p,

        /** HTML Tag. */
        script,

        /** HTML Tag. */
        span,

        /** HTML Tag. */
        table,

        /** HTML Tag. */
        ul;
    }

    /** Enumeration of link/form targets. */
    public static enum Target {

        /** Target blank. */
        BLANK("_blank"),

        /** Unspecified target. */
        NONE(""),

        /** Target parent. */
        PARENT("_parent"),

        /** Target self. */
        SELF("_self"),

        /** Target top. */
        TOP("_top");

        /** The target representation. */
        private String m_representation;

        /**
         * Constructor.<p>
         *
         * @param representation the target representation
         */
        Target(String representation) {

            m_representation = representation;
        }

        /**
         * Returns the target representation.<p>
         * @return the target representation
         */
        public String getRepresentation() {

            return m_representation;
        }
    }

    /** Browser dependent DOM implementation. */
    private static DOMImpl domImpl;

    /** Browser dependent style implementation. */
    private static DocumentStyleImpl styleImpl;

    /**
     * Hidden constructor.<p>
     */
    private DomUtil() {

        // doing nothing
    }

    /**
     * Adds an overlay div to the element.<p>
     *
     * @param element the element
     */
    public static void addDisablingOverlay(Element element) {

        Element overlay = DOM.createDiv();
        overlay.addClassName(I_LayoutBundle.INSTANCE.generalCss().disablingOverlay());
        element.getStyle().setPosition(Position.RELATIVE);
        element.appendChild(overlay);
    }

    /**
     * Returns if the given client position is over the given element.<p>
     * Use <code>-1</code> for x or y to ignore one ordering orientation.<p>
     *
     * @param element the element
     * @param x the client x position, use <code>-1</code> to ignore x position
     * @param y the client y position, use <code>-1</code> to ignore y position
     *
     * @return <code>true</code> if the given position is over the given element
     */
    public static boolean checkPositionInside(Element element, int x, int y) {

        // ignore x / left-right values for x == -1
        if (x != -1) {
            // check if the mouse pointer is within the width of the target
            int left = DomUtil.getRelativeX(x, element);
            int offsetWidth = element.getOffsetWidth();
            if ((left <= 0) || (left >= offsetWidth)) {
                return false;
            }
        }
        // ignore y / top-bottom values for y == -1
        if (y != -1) {
            // check if the mouse pointer is within the height of the target
            int top = DomUtil.getRelativeY(y, element);
            int offsetHeight = element.getOffsetHeight();
            if ((top <= 0) || (top >= offsetHeight)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Removes the opacity attribute from the element's inline-style.<p>
     *
     * @param element the DOM element to manipulate
     */
    public static void clearOpacity(Element element) {

        getStyleImpl().clearOpacity(element);
    }

    /**
     * Clones the given element.<p>
     *
     * It creates a new element with the same tag, and sets the class attribute,
     * and sets the innerHTML.<p>
     *
     * @param element the element to clone
     *
     * @return the cloned element
     */
    public static com.google.gwt.user.client.Element clone(Element element) {

        com.google.gwt.user.client.Element elementClone = DOM.createElement(element.getTagName());
        elementClone.setClassName(element.getClassName());
        elementClone.setInnerHTML(element.getInnerHTML());
        return elementClone;
    }

    /**
     * Generates a closing tag.<p>
     *
     * @param tag the tag to use
     *
     * @return HTML code
     */
    public static String close(Tag tag) {

        return "</" + tag.name() + ">";
    }

    /**
     * This method will create an {@link com.google.gwt.user.client.Element} for the given HTML.
     * The HTML should have a single root tag, if not, the first tag will be used and all others discarded.<p>
     * Script-tags will be removed.<p>
     *
     * @param html the HTML to use for the element
     *
     * @return the created element
     *
     * @throws Exception if something goes wrong
     */
    public static com.google.gwt.user.client.Element createElement(String html) throws Exception {

        com.google.gwt.user.client.Element wrapperDiv = DOM.createDiv();
        wrapperDiv.setInnerHTML(html);
        com.google.gwt.user.client.Element elementRoot = (com.google.gwt.user.client.Element)wrapperDiv.getFirstChildElement();
        DOM.removeChild(wrapperDiv, elementRoot);
        // just in case we have a script tag outside the root HTML-tag
        while ((elementRoot != null) && (elementRoot.getTagName().toLowerCase().equals(Tag.script.name()))) {
            elementRoot = (com.google.gwt.user.client.Element)wrapperDiv.getFirstChildElement();
            DOM.removeChild(wrapperDiv, elementRoot);
        }
        if (elementRoot == null) {
            DebugLog.getInstance().printLine(
                "Could not create element as the given HTML has no appropriate root element");
            throw new IllegalArgumentException(
                "Could not create element as the given HTML has no appropriate root element");
        }
        return elementRoot;

    }

    /**
     * Convenience method to assemble the HTML to use for a button face.<p>
     *
     * @param text text the up face text to set, set to <code>null</code> to not show any
     * @param imageClass the up face image class to use, set to <code>null</code> to not show any
     * @param align the alignment of the text in reference to the image
     *
     * @return the HTML
     */
    public static String createFaceHtml(String text, String imageClass, HorizontalAlignmentConstant align) {

        StringBuffer sb = new StringBuffer();
        if (align == HasHorizontalAlignment.ALIGN_LEFT) {
            if (ClientStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
                sb.append(text.trim());
            }
        }
        if (ClientStringUtil.isNotEmptyOrWhitespaceOnly(imageClass)) {
            String clazz = imageClass;
            if (ClientStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
                if (align == HasHorizontalAlignment.ALIGN_LEFT) {
                    clazz += " " + I_LayoutBundle.INSTANCE.buttonCss().spacerLeft();
                } else {
                    clazz += " " + I_LayoutBundle.INSTANCE.buttonCss().spacerRight();
                }
            }
            AttributeValue attr = new AttributeValue(Attribute.clazz, clazz);
            sb.append(enclose(Tag.span, "", attr));
        }
        if (align == HasHorizontalAlignment.ALIGN_RIGHT) {
            if (ClientStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
                sb.append(text.trim());
            }
        }
        return sb.toString();
    }

    /**
     * Creates an iFrame element with the given name attribute.<p>
     *
     * @param name the name attribute value
     *
     * @return the iFrame element
     */
    public static com.google.gwt.dom.client.Element createIFrameElement(String name) {

        return getDOMImpl().createIFrameElement(Document.get(), name);
    }

    /**
     * Encloses the given text with the given tag.<p>
     *
     * @param tag the tag to use
     * @param text the text to enclose
     * @param attrs the optional tag attributes
     *
     * @return HTML code
     */
    public static String enclose(Tag tag, String text, AttributeValue... attrs) {

        return open(tag, attrs) + text + close(tag);
    }

    /**
     * Triggers a mouse-out event for the given element.<p>
     *
     * Useful in case something is capturing all events.<p>
     *
     * @param element the element to use
     */
    public static void ensureMouseOut(Element element) {

        NativeEvent nativeEvent = Document.get().createMouseOutEvent(0, 0, 0, 0, 0, false, false, false, false, 0, null);
        element.dispatchEvent(nativeEvent);
    }

    /**
     * Triggers a mouse-out event for the given target.<p>
     *
     * Useful in case something is capturing all events.<p>
     *
     * @param target the target to use
     */
    public static void ensureMouseOut(HasHandlers target) {

        NativeEvent nativeEvent = Document.get().createMouseOutEvent(0, 0, 0, 0, 0, false, false, false, false, 0, null);
        DomEvent.fireNativeEvent(nativeEvent, target);
    }

    /**
     * Triggers a mouse-over event for the given element.<p>
     *
     * Useful in case something is capturing all events.<p>
     *
     * @param element the element to use
     */
    public static void ensureMouseOver(Element element) {

        NativeEvent nativeEvent = Document.get().createMouseOverEvent(
            0,
            0,
            0,
            0,
            0,
            false,
            false,
            false,
            false,
            0,
            null);
        element.dispatchEvent(nativeEvent);
    }

    /**
     * Checks the window.document for given style-sheet and includes it if required.<p>
     *
     * @param styleSheetLink the style-sheet link
     */
    public static native void ensureStyleSheetIncluded(String styleSheetLink)/*-{
        var styles = $wnd.document.styleSheets;
        for ( var i = 0; i < styles.length; i++) {
            if (styles[i].href != null
                    && styles[i].href.indexOf(styleSheetLink) >= 0) {
                // style-sheet is present
                return;
            }
        }
        // include style-sheet into head
        var headID = $wnd.document.getElementsByTagName("head")[0];
        var cssNode = $wnd.document.createElement('link');
        cssNode.type = 'text/css';
        cssNode.rel = 'stylesheet';
        cssNode.href = styleSheetLink;
        headID.appendChild(cssNode);
    }-*/;

    /**
     * Ensures that the given element is visible.<p>
     *
     * Assuming the scrollbars are on the container element, and that the element is a child of the container element.<p>
     *
     * @param containerElement the container element, has to be parent of the element
     * @param element the element to be seen
     * @param animationTime the animation time for scrolling, use zero for no animation
     */
    public static void ensureVisible(final Element containerElement, Element element, int animationTime) {

        Element item = element;
        int realOffset = 0;
        while ((item != null) && (item != containerElement)) {
            realOffset += element.getOffsetTop();
            item = item.getOffsetParent();
        }
        final int endScrollTop = realOffset - (containerElement.getOffsetHeight() / 2);

        if (animationTime <= 0) {
            // no animation
            containerElement.setScrollTop(endScrollTop);
            return;
        }
        final int startScrollTop = containerElement.getScrollTop();
        (new Animation() {

            /**
             * @see com.google.gwt.animation.client.Animation#onUpdate(double)
             */
            @Override
            protected void onUpdate(double progress) {

                containerElement.setScrollTop(startScrollTop + (int)((endScrollTop - startScrollTop) * progress));
            }
        }).run(animationTime);
    }

    /**
     * Ensures any embedded flash players are set opaque so UI elements may be placed above them.<p>
     *
     * @param element the element to work on
     */
    public static native void fixFlashZindex(Element element)/*-{

        var embeds = element.getElementsByTagName('embed');
        for (i = 0; i < embeds.length; i++) {
            embed = embeds[i];
            var new_embed;
            // everything but Firefox & Konqueror
            if (embed.outerHTML) {
                var html = embed.outerHTML;
                // replace an existing wmode parameter
                if (html.match(/wmode\s*=\s*('|")[a-zA-Z]+('|")/i))
                    new_embed = html.replace(/wmode\s*=\s*('|")window('|")/i,
                            "wmode='transparent'");
                // add a new wmode parameter
                else
                    new_embed = html.replace(/<embed\s/i,
                            "<embed wmode='transparent' ");
                // replace the old embed object with the fixed version
                embed.insertAdjacentHTML('beforeBegin', new_embed);
                embed.parentNode.removeChild(embed);
            } else {
                // cloneNode is buggy in some versions of Safari & Opera, but works fine in FF
                new_embed = embed.cloneNode(true);
                if (!new_embed.getAttribute('wmode')
                        || new_embed.getAttribute('wmode').toLowerCase() == 'window')
                    new_embed.setAttribute('wmode', 'transparent');
                embed.parentNode.replaceChild(new_embed, embed);
            }
        }
        // loop through every object tag on the site
        var objects = element.getElementsByTagName('object');
        for (i = 0; i < objects.length; i++) {
            object = objects[i];
            var new_object;
            // object is an IE specific tag so we can use outerHTML here
            if (object.outerHTML) {
                var html = object.outerHTML;
                // replace an existing wmode parameter
                if (html
                        .match(/<param\s+name\s*=\s*('|")wmode('|")\s+value\s*=\s*('|")[a-zA-Z]+('|")\s*\/?\>/i))
                    new_object = html
                            .replace(
                                    /<param\s+name\s*=\s*('|")wmode('|")\s+value\s*=\s*('|")window('|")\s*\/?\>/i,
                                    "<param name='wmode' value='transparent' />");
                // add a new wmode parameter
                else
                    new_object = html
                            .replace(/<\/object\>/i,
                                    "<param name='wmode' value='transparent' />\n</object>");
                // loop through each of the param tags
                var children = object.childNodes;
                for (j = 0; j < children.length; j++) {
                    try {
                        if (children[j] != null) {
                            var theName = children[j].getAttribute('name');
                            if (theName != null && theName.match(/flashvars/i)) {
                                new_object = new_object
                                        .replace(
                                                /<param\s+name\s*=\s*('|")flashvars('|")\s+value\s*=\s*('|")[^'"]*('|")\s*\/?\>/i,
                                                "<param name='flashvars' value='"
                                                        + children[j]
                                                                .getAttribute('value')
                                                        + "' />");
                            }
                        }
                    } catch (err) {
                    }
                }
                // replace the old embed object with the fixed versiony
                object.insertAdjacentHTML('beforeBegin', new_object);
                object.parentNode.removeChild(object);
            }
        }

    }-*/;

    /**
     * Generates a form element with hidden input fields.<p>
     *
     * @param action the form action
     * @param method the form method
     * @param target the form target
     * @param values the input values
     *
     * @return the generated form element
     */
    public static FormElement generateHiddenForm(String action, Method method, String target, Map<String, String> values) {

        FormElement formElement = Document.get().createFormElement();
        formElement.setMethod(method.name());
        if (ClientStringUtil.isNotEmptyOrWhitespaceOnly(target)) {
            formElement.setTarget(target);
        }
        formElement.setAction(action);
        for (Entry<String, String> input : values.entrySet()) {
            formElement.appendChild(createHiddenInput(input.getKey(), input.getValue()));
        }
        return formElement;
    }

    /**
     * Generates a form element with hidden input fields.<p>
     *
     * @param action the form action
     * @param method the form method
     * @param target the form target
     * @param values the input values
     *
     * @return the generated form element
     */
    public static FormElement generateHiddenForm(String action, Method method, Target target, Map<String, String> values) {

        return generateHiddenForm(action, method, target.getRepresentation(), values);
    }

    /**
     * Returns the given element or it's closest ancestor with the given class.<p>
     *
     * Returns <code>null</code> if no appropriate element was found.<p>
     *
     * @param element the element
     * @param className the class name
     *
     * @return the matching element
     */
    public static Element getAncestor(Element element, String className) {

        if (hasClass(className, element)) {
            return element;
        }
        if (element.getTagName().equalsIgnoreCase(Tag.body.name())) {
            return null;
        }
        return getAncestor(element.getParentElement(), className);
    }

    /**
     * Returns the given element or it's closest ancestor with the given tag name.<p>
     *
     * Returns <code>null</code> if no appropriate element was found.<p>
     *
     * @param element the element
     * @param tag the tag name
     *
     * @return the matching element
     */
    public static Element getAncestor(Element element, Tag tag) {

        if ((element == null) || (tag == null)) {
            return null;
        }
        if (element.getTagName().equalsIgnoreCase(tag.name())) {
            return element;
        }
        if (element.getTagName().equalsIgnoreCase(Tag.body.name())) {
            return null;
        }
        return getAncestor(element.getParentElement(), tag);
    }

    /**
     * Returns the given element or it's closest ancestor with the given tag and class.<p>
     *
     * Returns <code>null</code> if no appropriate element was found.<p>
     *
     * @param element the element
     * @param tag the tag name
     * @param className the class name
     *
     * @return the matching element
     */
    public static Element getAncestor(Element element, Tag tag, String className) {

        if (element.getTagName().equalsIgnoreCase(tag.name()) && hasClass(className, element)) {
            return element;
        }
        if (element.getTagName().equalsIgnoreCase(Tag.body.name())) {
            return null;
        }
        return getAncestor(element.getParentElement(), tag, className);
    }

    /**
     * Returns the computed style of the given element.<p>
     *
     * @param element the element
     * @param style the CSS property
     *
     * @return the currently computed style
     */
    public static String getCurrentStyle(Element element, Style style) {

        return getStyleImpl().getCurrentStyle(element, style.toString());
    }

    /**
     * Returns the computed style of the given element as floating point number.<p>
     *
     * @param element the element
     * @param style the CSS property
     *
     * @return the currently computed style
     */
    public static double getCurrentStyleFloat(Element element, Style style) {

        String currentStyle = getCurrentStyle(element, style);
        return ClientStringUtil.parseFloat(currentStyle);
    }

    /**
     * Returns the computed style of the given element as number.<p>
     *
     * @param element the element
     * @param style the CSS property
     *
     * @return the currently computed style
     */
    public static int getCurrentStyleInt(Element element, Style style) {

        String currentStyle = getCurrentStyle(element, style);
        return ClientStringUtil.parseInt(currentStyle);
    }

    /**
     * Determines the position of the list collector editable content.<p>
     *
     * @param editable the editable marker tag
     *
     * @return the position
     */
    public static PositionBean getEditablePosition(Element editable) {

        PositionBean result = new PositionBean();
        int dummy = -999;
        // setting minimum height
        result.setHeight(20);
        result.setWidth(60);
        result.setLeft(dummy);
        result.setTop(dummy);
        Element sibling = editable.getNextSiblingElement();
        while ((sibling != null)
            && !DomUtil.hasClass("cms-editable", sibling)
            && !DomUtil.hasClass("cms-editable-end", sibling)) {
            // only consider element nodes
            if ((sibling.getNodeType() == Node.ELEMENT_NODE)
                && !sibling.getTagName().equalsIgnoreCase(Tag.script.name())) {
                PositionBean siblingPos = PositionBean.generatePositionInfo(sibling);
                result.setLeft(((result.getLeft() == dummy) || (siblingPos.getLeft() < result.getLeft()))
                ? siblingPos.getLeft()
                : result.getLeft());
                result.setTop(((result.getTop() == dummy) || (siblingPos.getTop() < result.getTop()))
                ? siblingPos.getTop()
                : result.getTop());
                result.setHeight(((result.getTop() + result.getHeight()) > (siblingPos.getTop() + siblingPos.getHeight()))
                ? result.getHeight()
                : (siblingPos.getTop() + siblingPos.getHeight()) - result.getTop());
                result.setWidth(((result.getLeft() + result.getWidth()) > (siblingPos.getLeft() + siblingPos.getWidth()))
                ? result.getWidth()
                : (siblingPos.getLeft() + siblingPos.getWidth()) - result.getLeft());
            }
            sibling = sibling.getNextSiblingElement();
        }
        if ((result.getTop() == dummy) && (result.getLeft() == dummy)) {
            result = PositionBean.generatePositionInfo(editable);
        }
        if (result.getHeight() == -1) {
            // in case no height was set
            result = PositionBean.generatePositionInfo(editable);
            result.setHeight(20);
            result.setWidth((result.getWidth() < 60) ? 60 : result.getWidth());
        }

        return result;
    }

    /**
     * Utility method to determine the effective background color.<p>
     *
     * @param element the element
     *
     * @return the background color
     */
    public static String getEffectiveBackgroundColor(Element element) {

        String backgroundColor = DomUtil.getCurrentStyle(element, Style.backgroundColor);
        if ((ClientStringUtil.isEmptyOrWhitespaceOnly(backgroundColor) || isTransparent(backgroundColor) || backgroundColor.equals(StyleValue.inherit.toString()))) {
            if (Document.get().getBody() != element) {
                backgroundColor = getEffectiveBackgroundColor(element.getParentElement());
            } else {
                // if body element has still no background color set default to white
                backgroundColor = "#FFFFFF";
            }
        }

        return backgroundColor;
    }

    /**
     * Returns all elements from the DOM with the given CSS class.<p>
     *
     * @param className the class name to look for
     *
     * @return the matching elements
     */
    public static List<Element> getElementsByClass(String className) {

        return getElementsByClass(className, Tag.ALL, Document.get().getBody());
    }

    /**
     * Returns all elements with the given CSS class including the root element.<p>
     *
     * @param className the class name to look for
     * @param rootElement the root element of the search
     *
     * @return the matching elements
     */
    public static List<Element> getElementsByClass(String className, Element rootElement) {

        return getElementsByClass(className, Tag.ALL, rootElement);

    }

    /**
     * Returns all elements from the DOM with the given CSS class and tag name.<p>
     *
     * @param className the class name to look for
     * @param tag the tag
     *
     * @return the matching elements
     */
    public static List<Element> getElementsByClass(String className, Tag tag) {

        return getElementsByClass(className, tag, Document.get().getBody());
    }

    /**
     * Returns all elements with the given CSS class and tag name including the root element.<p>
     *
     * @param className the class name to look for
     * @param tag the tag
     * @param rootElement the root element of the search
     *
     * @return the matching elements
     */
    public static List<Element> getElementsByClass(String className, Tag tag, Element rootElement) {

        if ((rootElement == null) || (className == null) || (className.trim().length() == 0) || (tag == null)) {
            return null;
        }
        className = className.trim();
        List<Element> result = new ArrayList<Element>();
        if (internalHasClass(className, rootElement)) {
            result.add(rootElement);
        }
        NodeList<Element> elements = rootElement.getElementsByTagName(tag.toString());
        for (int i = 0; i < elements.getLength(); i++) {
            if (internalHasClass(className, elements.getItem(i))) {
                result.add(elements.getItem(i));
            }
        }
        return result;
    }

    /**
     * Returns the element position relative to its siblings.<p>
     *
     * @param e the element to get the position for
     *
     * @return the position, or <code>-1</code> if not found
     */
    public static int getPosition(Element e) {

        NodeList<Node> childNodes = e.getParentElement().getChildNodes();
        for (int i = childNodes.getLength(); i >= 0; i--) {
            if (childNodes.getItem(i) == e) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns the next ancestor to the element with an absolute, fixed or relative position.<p>
     *
     * @param child the element
     *
     * @return the positioning parent element (may be <code>null</code>)
     */
    public static Element getPositioningParent(Element child) {

        Element parent = child.getParentElement();
        while (parent != null) {
            String parentPositioning = DomUtil.getCurrentStyle(parent, Style.position);
            if (Position.RELATIVE.getCssName().equals(parentPositioning)
                || Position.ABSOLUTE.getCssName().equals(parentPositioning)
                || Position.FIXED.getCssName().equals(parentPositioning)) {
                return parent;
            }
            parent = parent.getParentElement();
        }
        return RootPanel.getBodyElement();
    }

    /**
     * Gets the horizontal position of the given x-coordinate relative to a given element.<p>
     *
     * @param x the coordinate to use
     * @param target the element whose coordinate system is to be used
     *
     * @return the relative horizontal position
     *
     * @see com.google.gwt.event.dom.client.MouseEvent#getRelativeX(com.google.gwt.dom.client.Element)
     */
    public static int getRelativeX(int x, Element target) {

        return (x - target.getAbsoluteLeft())
            + /* target.getScrollLeft() + */target.getOwnerDocument().getScrollLeft();
    }

    /**
     * Gets the vertical position of the given y-coordinate relative to a given element.<p>
     *
     * @param y the coordinate to use
     * @param target the element whose coordinate system is to be used
     *
     * @return the relative vertical position
     *
     * @see com.google.gwt.event.dom.client.MouseEvent#getRelativeY(com.google.gwt.dom.client.Element)
     */
    public static int getRelativeY(int y, Element target) {

        return (y - target.getAbsoluteTop()) + /* target.getScrollTop() +*/target.getOwnerDocument().getScrollTop();
    }

    /**
     * Returns the DOM window object.<p>
     *
     * @return the DOM window object
     */
    public static native JavaScriptObject getWindow() /*-{
        return $wnd;
    }-*/;

    /**
     * Returns the Z index from the given style.<p>
     *
     * This is a workaround for a bug with {@link com.google.gwt.dom.client.Style#getZIndex()} which occurs with IE in
     * hosted mode.<p>
     *
     * @param style the style object from which the Z index property should be fetched
     *
     * @return the z index
     */
    public static native String getZIndex(com.google.gwt.dom.client.Style style)
    /*-{
        return "" + style.zIndex;
    }-*/;

    /**
     * Utility method to determine if the given element has a set background.<p>
     *
     * @param element the element
     *
     * @return <code>true</code> if the element has a background set
     */
    public static boolean hasBackground(Element element) {

        String backgroundColor = DomUtil.getCurrentStyle(element, Style.backgroundColor);
        String backgroundImage = DomUtil.getCurrentStyle(element, Style.backgroundImage);
        if ((isTransparent(backgroundColor))
            && ((backgroundImage == null) || (backgroundImage.trim().length() == 0) || backgroundImage.equals(StyleValue.none.toString()))) {
            return false;
        }
        return true;
    }

    /**
     * Utility method to determine if the given element has a set background image.<p>
     *
     * @param element the element
     *
     * @return <code>true</code> if the element has a background image set
     */
    public static boolean hasBackgroundImage(Element element) {

        String backgroundImage = DomUtil.getCurrentStyle(element, Style.backgroundImage);
        if ((backgroundImage == null)
            || (backgroundImage.trim().length() == 0)
            || backgroundImage.equals(StyleValue.none.toString())) {
            return false;
        }
        return true;
    }

    /**
     * Utility method to determine if the given element has a set border.<p>
     *
     * @param element the element
     *
     * @return <code>true</code> if the element has a border
     */
    public static boolean hasBorder(Element element) {

        String borderStyle = DomUtil.getCurrentStyle(element, Style.borderStyle);
        if ((borderStyle == null) || borderStyle.equals(StyleValue.none.toString()) || (borderStyle.length() == 0)) {
            return false;
        }
        return true;

    }

    /**
     * Indicates if the given element has a CSS class.<p>
     *
     * @param className the class name to look for
     * @param element the element
     *
     * @return <code>true</code> if the element has the given CSS class
     */
    public static boolean hasClass(String className, Element element) {

        return internalHasClass(className.trim(), element);
    }

    /**
     * Returns if the given element has any dimension.<p>
     * All visible elements should have a dimension.<p>
     *
     * @param element the element to test
     *
     * @return <code>true</code> if the given element has any dimension
     */
    public static boolean hasDimension(Element element) {

        return (element.getOffsetHeight() > 0) || (element.getOffsetWidth() > 0);
    }

    /**
     * Gives an element the overflow:auto property.<p>
     *
     * @param elem a DOM element
     */
    public static void makeScrollable(Element elem) {

        elem.getStyle().setOverflow(Overflow.AUTO);
    }

    /**
     * Gives the element of a widget the overflow:auto property.<p>
     *
     * @param widget the widget to make scrollable
     */
    public static void makeScrollable(Widget widget) {

        makeScrollable(widget.getElement());
    }

    /**
     * Generates an opening tag.<p>
     *
     * @param tag the tag to use
     * @param attrs the optional tag attributes
     *
     * @return HTML code
     */
    public static String open(Tag tag, AttributeValue... attrs) {

        StringBuffer sb = new StringBuffer();
        sb.append("<").append(tag.name());
        for (AttributeValue attr : attrs) {
            sb.append(" ").append(attr.toString());
        }
        sb.append(">");
        return sb.toString();
    }

    /**
     * Positions an element in the DOM relative to another element.<p>
     *
     * @param elem the element to position
     * @param referenceElement the element relative to which the first element should be positioned
     * @param dx the x offset relative to the reference element
     * @param dy the y offset relative to the reference element
     */
    public static void positionElement(Element elem, Element referenceElement, int dx, int dy) {

        com.google.gwt.dom.client.Style style = elem.getStyle();
        style.setLeft(0, Unit.PX);
        style.setTop(0, Unit.PX);
        int myX = elem.getAbsoluteLeft();
        int myY = elem.getAbsoluteTop();
        int refX = referenceElement.getAbsoluteLeft();
        int refY = referenceElement.getAbsoluteTop();
        int newX = (refX - myX) + dx;
        int newY = (refY - myY) + dy;
        style.setLeft(newX, Unit.PX);
        style.setTop(newY, Unit.PX);
    }

    /**
     * Positions an element inside the given parent, reordering the content of the parent and returns the new position index.<p>
     * This is none absolute positioning. Use for drag and drop reordering of drop targets.<p>
     * Use <code>-1</code> for x or y to ignore one ordering orientation.<p>
     *
     * @param element the child element
     * @param parent the parent element
     * @param currentIndex the current index position of the element, use -1 if element is not attached to the parent yet
     * @param x the client x position, use <code>-1</code> to ignore x position
     * @param y the client y position, use <code>-1</code> to ignore y position
     *
     * @return the new index position
     */
    public static int positionElementInside(Element element, Element parent, int currentIndex, int x, int y) {

        if ((x == -1) && (y == -1)) {
            // this is wrong usage, do nothing
            DebugLog.getInstance().printLine("this is wrong usage, doing nothing");
            return currentIndex;
        }
        int indexCorrection = 0;
        int previousTop = 0;
        for (int index = 0; index < parent.getChildCount(); index++) {
            Node node = parent.getChild(index);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            Element child = (Element)node;
            if (child == element) {
                indexCorrection = 1;
            }
            String positioning = child.getStyle().getPosition();
            if (Position.ABSOLUTE.getCssName().equals(positioning) || Position.FIXED.getCssName().equals(positioning)) {
                // only not 'position:absolute' elements into account,
                // not visible children will be excluded in the next condition
                continue;
            }
            int left = 0;
            int width = 0;
            int top = 0;
            int height = 0;
            if (y != -1) {
                // check if the mouse pointer is within the height of the element
                top = DomUtil.getRelativeY(y, child);
                height = child.getOffsetHeight();
                if ((top <= 0) || (top >= height)) {
                    previousTop = top;
                    continue;
                }
            }
            if (x != -1) {
                // check if the mouse pointer is within the width of the element
                left = DomUtil.getRelativeX(x, child);
                width = child.getOffsetWidth();
                if ((left <= 0) || (left >= width)) {
                    previousTop = top;
                    continue;
                }
            }

            boolean floatSort = false;
            String floating = "";
            if ((top != 0) && (top == previousTop)) {
                floating = getCurrentStyle(child, Style.floatCss);
                if ("left".equals(floating) || "right".equals(floating)) {
                    floatSort = true;
                }
            }
            previousTop = top;
            if (child == element) {
                return currentIndex;
            }
            if ((y == -1) || floatSort) {
                boolean insertBefore = false;
                if (left < (width / 2)) {
                    if (!(floatSort && "right".equals(floating))) {
                        insertBefore = true;
                    }
                } else if (floatSort && "right".equals(floating)) {
                    insertBefore = true;
                }
                if (insertBefore) {
                    parent.insertBefore(element, child);
                    currentIndex = index - indexCorrection;
                    return currentIndex;
                } else {
                    parent.insertAfter(element, child);
                    currentIndex = (index + 1) - indexCorrection;
                    return currentIndex;
                }
            }
            if (top < (height / 2)) {
                parent.insertBefore(element, child);
                currentIndex = index - indexCorrection;
                return currentIndex;
            } else {
                parent.insertAfter(element, child);
                currentIndex = (index + 1) - indexCorrection;
                return currentIndex;
            }

        }
        // not over any child position
        if ((currentIndex >= 0) && (element.getParentElement() == parent)) {
            // element is already attached to this parent and no new position available
            // don't do anything
            return currentIndex;
        }
        int top = DomUtil.getRelativeY(y, parent);
        int offsetHeight = parent.getOffsetHeight();
        if ((top >= (offsetHeight / 2))) {
            // over top half, insert as first child
            parent.insertFirst(element);
            currentIndex = 0;
            return currentIndex;
        }
        // over bottom half, insert as last child
        parent.appendChild(element);
        currentIndex = parent.getChildCount() - 1;
        return currentIndex;
    }

    /**
     * Removes any present overlay from the element and it's children.<p>
     *
     * @param element the element
     */
    public static void removeDisablingOverlay(Element element) {

        List<Element> overlays = DomUtil.getElementsByClass(
            I_LayoutBundle.INSTANCE.generalCss().disablingOverlay(),
            Tag.div,
            element);
        if (overlays == null) {
            return;
        }
        for (Element overlay : overlays) {
            overlay.getParentElement().getStyle().clearPosition();
            overlay.removeFromParent();
        }
        element.removeClassName(I_LayoutBundle.INSTANCE.generalCss().hideOverlay());
    }

    /**
     * Removes all script tags from the given element.<p>
     *
     * @param element the element to remove the script tags from
     *
     * @return the resulting element
     */
    public static Element removeScriptTags(Element element) {

        NodeList<Element> scriptTags = element.getElementsByTagName(Tag.script.name());
        // iterate backwards over list to ensure all tags get removed
        for (int i = scriptTags.getLength() - 1; i >= 0; i--) {
            scriptTags.getItem(i).removeFromParent();
        }
        return element;
    }

    /**
     * Removes all script tags from the given string.<p>
     *
     * @param source the source string
     *
     * @return the resulting string
     */
    public static native String removeScriptTags(String source)/*-{

        var matchTag = /<script[^>]*?>[\s\S]*?<\/script>/g;
        return source.replace(matchTag, "");
    }-*/;

    /**
     * Sets an attribute on a Javascript object.<p>
     *
     * @param jso the Javascript object
     * @param key the attribute name
     * @param value the new attribute value
     */
    public static native void setAttribute(JavaScriptObject jso, String key, JavaScriptObject value) /*-{
        jso[key] = value;
    }-*/;

    /**
     * Sets an attribute on a Javascript object.<p>
     *
     * @param jso the Javascript object
     * @param key the attribute name
     * @param value the new attribute value
     */
    public static native void setAttribute(JavaScriptObject jso, String key, String value) /*-{
        jso[key] = value;
    }-*/;

    /**
     * Sets a CSS class to show or hide a given overlay. Will not add an overlay to the element.<p>
     *
     * @param element the parent element of the overlay
     * @param show <code>true</code> to show the overlay
     */
    public static void showOverlay(Element element, boolean show) {

        if (show) {
            element.removeClassName(I_LayoutBundle.INSTANCE.generalCss().hideOverlay());
        } else {
            element.addClassName(I_LayoutBundle.INSTANCE.generalCss().hideOverlay());
        }
    }

    /**
     * Wraps a widget in a scrollable FlowPanel.<p>
    
     * @param widget the original widget
     * @return the wrapped widget
     */
    public static FlowPanel wrapScrollable(Widget widget) {

        FlowPanel wrapper = new FlowPanel();
        wrapper.add(widget);
        makeScrollable(wrapper);
        return wrapper;
    }

    /**
     * Creates a hidden input field with the given name and value.<p>
     *
     * @param name the field name
     * @param value the field value
     * @return the input element
     */
    private static InputElement createHiddenInput(String name, String value) {

        InputElement input = Document.get().createHiddenInputElement();
        input.setName(name);
        input.setValue(value);
        return input;
    }

    /**
     * Returns the DOM implementation.<p>
     *
     * @return the DOM implementation
     */
    private static DOMImpl getDOMImpl() {

        if (domImpl == null) {
            domImpl = GWT.create(DOMImpl.class);
        }
        return domImpl;
    }

    /**
     * Returns the document style implementation.<p>
     *
     * @return the document style implementation
     */
    private static DocumentStyleImpl getStyleImpl() {

        if (styleImpl == null) {
            styleImpl = GWT.create(DocumentStyleImpl.class);
        }
        return styleImpl;
    }

    /**
     * Internal method to indicate if the given element has a CSS class.<p>
     *
     * @param className the class name to look for
     * @param element the element
     *
     * @return <code>true</code> if the element has the given CSS class
     */
    private static boolean internalHasClass(String className, Element element) {

        String elementClass = element.getClassName().trim();
        boolean hasClass = elementClass.equals(className);
        hasClass |= elementClass.contains(" " + className + " ");
        hasClass |= elementClass.startsWith(className + " ");
        hasClass |= elementClass.endsWith(" " + className);

        return hasClass;
    }

    /**
     * Checks if the given color value is transparent.<p>
     *
     * @param backgroundColor the color value
     *
     * @return <code>true</code> if transparent
     */
    private static boolean isTransparent(String backgroundColor) {

        // not only check 'transparent' but also 'rgba(0, 0, 0, 0)' as returned by chrome
        return StyleValue.transparent.toString().equalsIgnoreCase(backgroundColor)
            || "rgba(0, 0, 0, 0)".equalsIgnoreCase(backgroundColor);
    }

}
TOP

Related Classes of com.alkacon.geranium.client.util.DomUtil$AttributeValue

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.