org.opencms.gwt.client.util.CmsDomUtil Maven / Gradle / Ivy
Show all versions of opencms-gwt Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (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
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* 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 org.opencms.gwt.client.util;
import org.opencms.gwt.client.CmsEditableDataJSO;
import org.opencms.gwt.client.I_CmsDescendantResizeHandler;
import org.opencms.gwt.client.Messages;
import org.opencms.gwt.client.ui.CmsAlertDialog;
import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle;
import org.opencms.gwt.client.util.impl.DOMImpl;
import org.opencms.gwt.client.util.impl.DocumentStyleImpl;
import org.opencms.util.CmsStringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.Lists;
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.
*
* @since 8.0.0
*/
public final class CmsDomUtil {
/**
* HTML tag attributes.
*/
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.
*/
public static class AttributeValue {
/** The attribute. */
private Attribute m_attr;
/** The attribute value. */
private String m_value;
/**
* Constructor.
*
* @param attr the attribute
*/
public AttributeValue(Attribute attr) {
this(attr, null);
}
/**
* Constructor.
*
* @param attr the attribute
* @param value the value
*/
public AttributeValue(Attribute attr, String value) {
m_attr = attr;
m_value = value;
}
/**
* Returns the attribute.
*
* @return the attribute
*/
public Attribute getAttr() {
return m_attr;
}
/**
* Returns the value.
*
* @return the value
*/
public String getValue() {
return m_value;
}
/**
* Sets the value.
*
* @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.
*/
public static enum Color {
/** CSS Color. */
red;
}
/**
* HTML entities.
*/
public static enum Entity {
/** non-breaking space. */
hellip,
/** non-breaking space. */
nbsp;
/**
* Returns the HTML code for this entity.
*
* @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.
*/
public static enum Style {
/** CSS Property. */
backgroundColor,
/** CSS Property. */
backgroundImage,
/** CSS property. */
borderLeftWidth,
/** CSS property. */
borderRightWidth,
/** CSS Property. */
borderStyle,
/** CSS property. */
boxSizing,
/** 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. */
overflow,
/** CSS Property. */
padding,
/** CSS property. */
paddingLeft,
/** CSS property. */
paddingRight,
/** 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.
*/
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.
*/
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.
*
* @param representation the target representation
*/
Target(String representation) {
m_representation = representation;
}
/**
* Returns the target representation.
* @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;
/** The dynamic style sheet object. */
private static JavaScriptObject m_dynamicStyleSheet;
/**
* Hidden constructor.
*/
private CmsDomUtil() {
// doing nothing
}
/**
* Adds an overlay div to the element.
*
* @param element the element
*/
public static void addDisablingOverlay(Element element) {
Element overlay = DOM.createDiv();
overlay.addClassName(I_CmsLayoutBundle.INSTANCE.generalCss().disablingOverlay());
element.getStyle().setPosition(Position.RELATIVE);
element.appendChild(overlay);
}
/**
* Adds a CSS style rule to a dynamically inserted style sheet.
*
* @param rule the style rule
*/
public static native void addDynamicStyleRule(String rule) /*-{
var style = @org.opencms.gwt.client.util.CmsDomUtil::m_dynamicStyleSheet;
if (style == null) {
var style = $wnd.document.createElement("style");
style.appendChild($wnd.document.createTextNode(""));
$wnd.document.head.appendChild(style);
@org.opencms.gwt.client.util.CmsDomUtil::m_dynamicStyleSheet = style;
}
style.sheet.insertRule(rule, 0);
}-*/;
/**
* Returns if the given client position is over the given element.
* Use -1
for x or y to ignore one ordering orientation.
*
* @param element the element
* @param x the client x position, use -1
to ignore x position
* @param y the client y position, use -1
to ignore y position
*
* @return true
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 = CmsDomUtil.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 = CmsDomUtil.getRelativeY(y, element);
int offsetHeight = element.getOffsetHeight();
if ((top <= 0) || (top >= offsetHeight)) {
return false;
}
}
return true;
}
/**
* Clears the elements hover state by removing it from the DOM and re-attaching it.
*
* @param element the element
*/
public static void clearHover(Element element) {
Element parent = element.getParentElement();
Element sibling = element.getNextSiblingElement();
element.removeFromParent();
parent.insertBefore(element, sibling);
}
/**
* Removes the opacity attribute from the element's inline-style.
*
* @param element the DOM element to manipulate
*/
public static void clearOpacity(Element element) {
getStyleImpl().clearOpacity(element);
}
/**
* Clones the given element.
*
* It creates a new element with the same tag, and sets the class attribute,
* and sets the innerHTML.
*
* @param element the element to clone
*
* @return the cloned element
*/
public static Element clone(Element element) {
Element elementClone = DOM.createElement(element.getTagName());
elementClone.setClassName(element.getClassName());
elementClone.setInnerHTML(element.getInnerHTML());
return elementClone;
}
/**
* Generates a closing tag.
*
* @param tag the tag to use
*
* @return HTML code
*/
public static String close(Tag tag) {
return "" + tag.name() + ">";
}
/**
* Copy the text content of the matching element to the clip-board.
*
* @param selector the query selector matching the target element
*
* @return in case the command was executed successfully
*/
public static native boolean copyToClipboard(String selector)/*-{
var doc = $wnd.document;
var targetElement = doc.querySelector(selector);
if (targetElement != null) {
var text = targetElement.textContent;
var textArea = document.createElement("textarea");
// add some styles to hide the text area
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.width = '2em';
textArea.style.height = '2em';
textArea.style.padding = 0;
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
textArea.style.color = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
var result = false;
try {
result = document.execCommand('copy');
} catch (err) {
}
document.body.removeChild(textArea);
return result;
}
}-*/;
/**
* This method will create an {@link com.google.gwt.dom.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.
* Script-tags will be removed.
*
* @param html the HTML to use for the element
*
* @return the created element
*
* @throws Exception if something goes wrong
*/
public static Element createElement(String html) throws Exception {
Element wrapperDiv = DOM.createDiv();
wrapperDiv.setInnerHTML(html);
Element elementRoot = wrapperDiv.getFirstChildElement();
wrapperDiv.removeChild(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 = wrapperDiv.getFirstChildElement();
wrapperDiv.removeChild(elementRoot);
}
if (elementRoot == null) {
CmsDebugLog.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.
*
* @param text text the up face text to set, set to null
to not show any
* @param imageClass the up face image class to use, set to null
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 (CmsStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
sb.append(text.trim());
}
}
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(imageClass)) {
String clazz = imageClass;
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
if (align == HasHorizontalAlignment.ALIGN_LEFT) {
clazz += " " + I_CmsLayoutBundle.INSTANCE.buttonCss().spacerLeft();
} else {
clazz += " " + I_CmsLayoutBundle.INSTANCE.buttonCss().spacerRight();
}
}
AttributeValue attr = new AttributeValue(Attribute.clazz, clazz);
sb.append(enclose(Tag.span, "", attr));
}
if (align == HasHorizontalAlignment.ALIGN_RIGHT) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(text)) {
sb.append(text.trim());
}
}
return sb.toString();
}
/**
* Creates an iFrame element with the given name attribute.
*
* @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.
*
* @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);
}
/**
* Ensures a script tag is present within the window document context.
*
* @param javascriptLink the link to the java script resource
*/
public static void ensureJavaScriptIncluded(String javascriptLink) {
if (!isJavaScriptPresent(javascriptLink)) {
injectScript(javascriptLink);
}
}
/**
* Triggers a mouse-out event for the given element.
*
* Useful in case something is capturing all events.
*
* @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.
*
* Useful in case something is capturing all events.
*
* @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.
*
* Useful in case something is capturing all events.
*
* @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.
*
* @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.
*
* Assuming the scrollbars are on the container element, and that the element is a child of the container element.
*
* @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);
}
/**
* Fires a focus event for the given widget.
*
* @param widget the widget
*/
public static void fireFocusEvent(Widget widget) {
NativeEvent nativeEvent = Document.get().createFocusEvent();
DomEvent.fireNativeEvent(nativeEvent, widget, widget.getElement());
}
/**
* Ensures any embedded flash players are set opaque so UI elements may be placed above them.
*
* @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(/