/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This 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 software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.gwt.wysiwyg.client.plugin.link; import org.xwiki.gwt.dom.client.DOMUtils; import org.xwiki.gwt.dom.client.Document; import org.xwiki.gwt.dom.client.DocumentFragment; import org.xwiki.gwt.dom.client.Element; import org.xwiki.gwt.dom.client.InnerHTMLListener; import org.xwiki.gwt.dom.client.Text; import org.xwiki.gwt.wysiwyg.client.plugin.link.LinkConfig.LinkType; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; /** * Analyzes the passed subtree and detects the link structures and transforms them in minimal HTML elements with meta * fragments attached, so that the editor operates with minimal HTML. * * @version $Id: 8c52d7c6a4f5df4a0ac3f587e83eb1896c1dd081 $ */ public class LinkMetaDataExtractor implements InnerHTMLListener { @Override public void onInnerHTMLChange(Element parent) { // look up all images in this subtree NodeList<com.google.gwt.dom.client.Element> anchors = parent.getElementsByTagName("a"); for (int i = 0; i < anchors.getLength(); i++) { Element anchor = (Element) anchors.getItem(i); processElement(anchor); } } /** * Processes the passed anchor looking for the wrapping span and neighbor comments to encapsulate it all in a meta * fragment and leave only the anchor in the tree. * * @param anchor the anchor element found */ private void processElement(Element anchor) { // Search for parent span and surrounding comments. Element parentNode = anchor.getParentElement().cast(); if (parentNode == null || !"span".equalsIgnoreCase(parentNode.getNodeName()) || LinkType.getByClassName(parentNode.getClassName()) == null || !hasLinkMarkers(parentNode)) { return; } DocumentFragment metaFragment = ((Document) anchor.getOwnerDocument()).createDocumentFragment(); // Move the link markers. metaFragment.appendChild(parentNode.getPreviousSibling()); metaFragment.appendChild(parentNode.getNextSibling()); // Create the place-holder and replace the anchor. Text placeholder = (Text) ((Document) anchor.getOwnerDocument()).createTextNode(Element.INNER_HTML_PLACEHOLDER); parentNode.replaceChild(placeholder, anchor); // Replace the parent node with the anchor. parentNode.getParentElement().replaceChild(anchor, parentNode); // Put the parent node in the meta fragment. metaFragment.insertAfter(parentNode, metaFragment.getFirstChild()); anchor.setMetaData(metaFragment); } /** * @param node a DOM node * @return {@code true} if the given node is wrapped by the link marker comments, {@code false} otherwise */ private boolean hasLinkMarkers(Node node) { return hasLinkStartMarker(node) && hasLinkStopMarker(node); } /** * @param node a DOM node * @return {@code true} if the given node is preceded by the link start marker, {@code false} otherwise */ private boolean hasLinkStartMarker(Node node) { Node previousSibling = node.getPreviousSibling(); return previousSibling != null && previousSibling.getNodeType() == DOMUtils.COMMENT_NODE && previousSibling.getNodeValue().startsWith("startwikilink"); } /** * @param node a DOM node * @return {@code true} if the given node is followed by the link stop marker, {@code false} otherwise */ private boolean hasLinkStopMarker(Node node) { Node nextSibling = node.getNextSibling(); return nextSibling != null && nextSibling.getNodeType() == DOMUtils.COMMENT_NODE && nextSibling.getNodeValue().startsWith("stopwikilink"); } }