// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.eclipse.che.ide.util.dom; import elemental.client.Browser; import elemental.dom.Document; import elemental.dom.Element; import elemental.dom.Text; import elemental.html.AnchorElement; import elemental.html.BRElement; import elemental.html.BodyElement; import elemental.html.ButtonElement; import elemental.html.CanvasElement; import elemental.html.DivElement; import elemental.html.FormElement; import elemental.html.HeadElement; import elemental.html.IFrameElement; import elemental.html.ImageElement; import elemental.html.InputElement; import elemental.html.LIElement; import elemental.html.ParagraphElement; import elemental.html.PreElement; import elemental.html.SpanElement; import elemental.html.TableCellElement; import elemental.html.TableElement; import elemental.html.TableRowElement; import elemental.html.TextAreaElement; import elemental.html.UListElement; import elemental.html.Window; import elemental.js.dom.JsElement; import elemental.ranges.Range; import elemental.svg.SVGAnimatedString; import org.eclipse.che.ide.util.StringUtils; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; import java.util.List; /** Simple utility class for shortening frequent calls to Elemental libraries. */ public class Elements { /** * A regular expression used by the {@link #markup(Element, String, String)} function to search * for http links and new lines. */ private static final RegExp REGEXP_MARKUP = RegExp.compile("(?:(?:(?:https?://)|(?:www\\.))[^\\s]+)|[\n]", "ig"); @SuppressWarnings("unchecked") public static <T extends com.google.gwt.dom.client.Element, R extends Element> R asJsElement( T element) { return (R)element.<JsElement>cast(); } public static AnchorElement createAnchorElement(String... classNames) { AnchorElement elem = getDocument().createAnchorElement(); addClassesToElement(elem, classNames); return elem; } public static ButtonElement createButtonElement(String... classNames) { ButtonElement elem = getDocument().createButtonElement(); addClassesToElement(elem, classNames); return elem; } public static BRElement createBRElement(String... classNames) { BRElement elem = getDocument().createBRElement(); addClassesToElement(elem, classNames); return elem; } public static CanvasElement createCanvas(String... classNames) { CanvasElement elem = getDocument().createCanvasElement(); addClassesToElement(elem, classNames); return elem; } public static DivElement createDivElement(String... classNames) { DivElement elem = getDocument().createDivElement(); addClassesToElement(elem, classNames); return elem; } public static Element createElement(String tagName, String... classNames) { Element elem = getDocument().createElement(tagName); addClassesToElement(elem, classNames); return elem; } public static FormElement createFormElement(String... classNames) { FormElement elem = getDocument().createFormElement(); addClassesToElement(elem, classNames); return elem; } public static TableElement createTableElement(String... classNames) { TableElement elem = getDocument().createTableElement(); addClassesToElement(elem, classNames); return elem; } public static TableRowElement createTRElement(String... classNames) { TableRowElement elem = (TableRowElement)getDocument().createElement("tr"); addClassesToElement(elem, classNames); return elem; } public static TableCellElement createTDElement(String... classNames) { TableCellElement elem = (TableCellElement)getDocument().createElement("td"); addClassesToElement(elem, classNames); return elem; } public static InputElement createInputElement(String... classNames) { InputElement elem = getDocument().createInputElement(); addClassesToElement(elem, classNames); return elem; } public static InputElement createInputTextElement(String... classNames) { InputElement elem = getDocument().createInputElement(); addClassesToElement(elem, classNames); elem.setType("text"); return elem; } public static IFrameElement createIFrameElement(String... classNames) { IFrameElement elem = getDocument().createIFrameElement(); addClassesToElement(elem, classNames); return elem; } public static ImageElement createImageElement(String... classNames) { ImageElement elem = getDocument().createImageElement(); addClassesToElement(elem, classNames); return elem; } public static SpanElement createSpanElement(String... classNames) { SpanElement elem = getDocument().createSpanElement(); addClassesToElement(elem, classNames); return elem; } public static TextAreaElement createTextAreaElement(String... classNames) { TextAreaElement elem = getDocument().createTextAreaElement(); addClassesToElement(elem, classNames); return elem; } public static ParagraphElement createParagraphElement(String... classNames) { ParagraphElement elem = getDocument().createParagraphElement(); addClassesToElement(elem, classNames); return elem; } public static PreElement createPreElement(String... classNames) { PreElement elem = getDocument().createPreElement(); addClassesToElement(elem, classNames); return elem; } public static LIElement createLiElement(String... classNames) { LIElement elem = getDocument().createLIElement(); addClassesToElement(elem, classNames); return elem; } public static UListElement createUListElement(String... classNames) { UListElement elem = getDocument().createUListElement(); addClassesToElement(elem, classNames); return elem; } public static Text createTextNode(String data) { return getDocument().createTextNode(data); } public static Element getActiveElement() { return getDocument().getActiveElement(); } public static Document getDocument() { return Browser.getDocument(); } public static Window getWindow() { return Browser.getWindow(); } public static BodyElement getBody() { return getBody(getDocument()); } public static BodyElement getBody(Document document) { return ((BodyElement)document.getBody()); } public static HeadElement getHead() { return getHead(getDocument()); } public static HeadElement getHead(Document document) { HeadElement result = document.getHead(); if (result == null) { // Some versions of Firefox return undefined for the document.head. result = (HeadElement)document.getElementsByTagName("head").item(0); } return result; } public static Element getElementById(String id) { return getDocument().getElementById(id); } public static void injectJs(String js) { Element scriptElem = createElement("script"); scriptElem.setAttribute("language", "javascript"); scriptElem.setTextContent(js); getBody().appendChild(scriptElem); } /** * Replaces the contents of an Element with the specified ID, with a single * element. * * @param id * The ID of the Element that we will erase the contents of. * @param with * The element that we will attach to the element that we just * erased the contents of. */ public static void replaceContents(String id, Element with) { Element parent = getElementById(id); replaceContents(parent, with); } /** Replaces the contents of a container with the given contents. */ public static void replaceContents(Element container, Element contents) { container.setInnerHTML(""); container.appendChild(contents); } public static void setCollideTitle(String subtitle) { if (StringUtils.isNullOrEmpty(subtitle)) { getDocument().setTitle("Collide"); } else { getDocument().setTitle(subtitle + " - Collide"); } } /** * Scans a string converting any recognizable html links into an anchor tag and replacing newlines * with a <br/>. Once built the result is appended to the provided element. */ // TODO: Long term we need a markdown engine :) public static void markup(Element e, String text, String linkCssClass) { e.setInnerHTML(""); List<String> paragraphs = StringUtils.split(text, "\n\n"); for (String paragraph : paragraphs) { markupParagraph(e, paragraph, linkCssClass); } } /** Creates a paragraph tag and fills it with spans and anchor tags internally. */ private static void markupParagraph(Element parent, String text, String linkCssClass) { if (StringUtils.isNullOrWhitespace(text)) { // don't add any dom here return; } ParagraphElement myParagraph = createParagraphElement(); int index = 0; REGEXP_MARKUP.setLastIndex(0); SpanElement current = createSpanElement(); for (MatchResult match = REGEXP_MARKUP.exec(text); match != null; match = REGEXP_MARKUP.exec(text)) { current.setTextContent(text.substring(index, match.getIndex())); myParagraph.appendChild(current); current = createSpanElement(); /* * If our match is a \n we need to create a <br/> element to force a line break, otherwise we * matched an http/www link so let's make an anchor tag out of it. */ if (match.getGroup(0).equals("\n")) { myParagraph.appendChild(createBRElement()); } else { AnchorElement anchor = createAnchorElement(linkCssClass); anchor.setHref(match.getGroup(0)); anchor.setTarget("_blank"); anchor.setTextContent(match.getGroup(0)); myParagraph.appendChild(anchor); } index = match.getIndex() + match.getGroup(0).length(); } current.setTextContent(text.substring(index)); myParagraph.appendChild(current); parent.appendChild(myParagraph); } /** Selects all text in the specified element. */ public static void selectAllText(Element e) { Range range = Browser.getDocument().createRange(); range.selectNode(e); Browser.getWindow().getSelection().addRange(range); } public static void addClassesToElement(Element e, String... classNames) { for (String className : classNames) { if (!StringUtils.isNullOrEmpty(className)) { addClassName(className, e); } } } public static boolean hasClassName(String className, Element element) { assert className != null : "Unexpected null class name"; final Object o = element.getClassName(); final String currentClassName; //don't remove this checking, some times we can catch NPE if we work with OMSVGSVGElement, //because getClassName on this class returns SVGAnimatedString, with stored classes, so //we need to check additionally type of input element if (o instanceof SVGAnimatedString) { currentClassName = ((SVGAnimatedString)o).getBaseVal(); } else { currentClassName = String.valueOf(o); } return (currentClassName != null) && (currentClassName.equals(className) || currentClassName.startsWith(className + " ") || currentClassName.endsWith(" " + className) || currentClassName.contains(" " + className + " ")); } public static void addClassName(String className, Element element) { assert (className != null) : "Unexpectedly null class name"; className = className.trim(); assert (className.length() != 0) : "Unexpectedly empty class name"; // Get the current style string. String oldClassName = element.getClassName(); int idx = oldClassName.indexOf(className); // Calculate matching index. while (idx != -1) { if (idx == 0 || oldClassName.charAt(idx - 1) == ' ') { int last = idx + className.length(); int lastPos = oldClassName.length(); if ((last == lastPos) || ((last < lastPos) && (oldClassName.charAt(last) == ' '))) { break; } } idx = oldClassName.indexOf(className, idx + 1); } // Only add the style if it's not already present. if (idx == -1) { if (oldClassName.length() > 0) { oldClassName += " "; } element.setClassName(oldClassName + className); } } public static void removeClassName(String className, Element element) { assert (className != null) : "Unexpectedly null class name"; className = className.trim(); assert (className.length() != 0) : "Unexpectedly empty class name"; // Get the current style string. String oldStyle = element.getClassName(); int idx = oldStyle.indexOf(className); // Calculate matching index. while (idx != -1) { if (idx == 0 || oldStyle.charAt(idx - 1) == ' ') { int last = idx + className.length(); int lastPos = oldStyle.length(); if ((last == lastPos) || ((last < lastPos) && (oldStyle.charAt(last) == ' '))) { break; } } idx = oldStyle.indexOf(className, idx + 1); } // Don't try to remove the style if it's not there. if (idx != -1) { // Get the leading and trailing parts, without the removed name. String begin = oldStyle.substring(0, idx).trim(); String end = oldStyle.substring(idx + className.length()).trim(); // Some contortions to make sure we don't leave extra spaces. String newClassName; if (begin.length() == 0) { newClassName = end; } else if (end.length() == 0) { newClassName = begin; } else { newClassName = begin + " " + end; } element.setClassName(newClassName); } } /** * Disable element text selection. Useful for disabling text selection in UI elements especially in main menu, etc. * * @param e * element * @param disable * true - if text selection should be disabled */ public native static void disableTextSelection(com.google.gwt.dom.client.Element e, boolean disable)/*-{ if (disable) { e.ondrag = function () { return false; }; e.onselectstart = function () { return false; }; e.style.MozUserSelect = "none" } else { e.ondrag = null; e.onselectstart = null; e.style.MozUserSelect = "text" } }-*/; private Elements() { } // COV_NF_LINE }