/*
* 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.DocumentFragment;
import org.xwiki.gwt.dom.client.Element;
import org.xwiki.gwt.user.client.EscapeUtils;
import org.xwiki.gwt.user.client.ui.rta.cmd.internal.AbstractInsertElementExecutable.ConfigDOMReader;
import org.xwiki.gwt.wysiwyg.client.plugin.image.ImageConfigDOMReader;
import org.xwiki.gwt.wysiwyg.client.plugin.link.LinkConfig.LinkType;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.ImageElement;
/**
* Creates an {@link LinkConfig} object from an {@link AnchorElement}.
*
* @version $Id: de2f9ff8dbe552487f0926e6ffc648f61063c281 $
*/
public class LinkConfigDOMReader implements ConfigDOMReader<LinkConfig, AnchorElement>
{
/**
* The string that separates link reference components.
*/
private static final String LINK_REFERENCE_COMPONENT_SEPARATOR = "|-|";
/**
* The object used to extract the image file name when the anchor wraps an image.
*/
private final ImageConfigDOMReader imageConfigHTMLParser = new ImageConfigDOMReader();
@Override
public LinkConfig read(AnchorElement anchor)
{
LinkConfig linkConfig = new LinkConfig();
// Get link meta data.
DocumentFragment linkMetadata = Element.as(anchor).getMetaData();
if (linkMetadata != null) {
// Process the meta data.
String startComment = linkMetadata.getChildNodes().getItem(0).getNodeValue();
Element wrappingSpan = (Element) linkMetadata.getChildNodes().getItem(1);
linkConfig.setReference(EscapeUtils.unescapeBackslash(startComment.substring("startwikilink:".length())));
linkConfig.setType(readLinkType(wrappingSpan, linkConfig.getReference()));
} else {
// It's an external link.
linkConfig.setType(LinkType.EXTERNAL);
}
// NOTE: We use getAttribute and not getHref to access the URL because we want the exact value of the attribute
// and not the resolved (absolute) URL.
linkConfig.setUrl(anchor.getAttribute("href"));
linkConfig.setLabel(anchor.getInnerHTML());
if (anchor.getChildNodes().getLength() == 1 && "img".equalsIgnoreCase(anchor.getFirstChild().getNodeName())) {
// The anchor wraps an image.
ImageElement image = (ImageElement) anchor.getFirstChild();
linkConfig.setLabelText(imageConfigHTMLParser.read(image).getReference());
linkConfig.setReadOnlyLabel(true);
} else {
linkConfig.setLabelText(anchor.getInnerText());
}
linkConfig.setTooltip(anchor.getTitle());
linkConfig.setOpenInNewWindow("__blank".equals(anchor.getRel()));
return linkConfig;
}
/**
* Parses a link type from its wrapping span and from its reference.
*
* @param wrappingSpan the link's wrapping span
* @param reference the link reference
* @return the link type, as parsed from it's wrapping span and from its reference
*/
private LinkType readLinkType(Element wrappingSpan, String reference)
{
LinkType linkType = LinkType.getByClassName(wrappingSpan.getClassName());
if (linkType == null) {
// Default to external link.
linkType = LinkType.EXTERNAL;
} else if (linkType == LinkType.EXTERNAL && "mailto".equalsIgnoreCase(getLinkProtocol(reference))) {
// Email links don't user a dedicated CSS class name.
linkType = LinkType.EMAIL;
}
return linkType;
}
/**
* Extracts the link protocol from a link reference.
* <p>
* NOTE: Ideally the client shouldn't know the syntax/format of the link reference. The link reference should be
* parsed on the server side. We need to extract the link protocol to be able to determine the link type which is
* then used to determine what link wizard to open when editing a link. Making an asynchronous request to parse the
* link reference to get only the link type/protocol before any link wizard is opened is a bit difficult with the
* current design. This needs to be fixed nevertheless. Until then we make the assumption that the link protocol is
* between the first and second occurrence of the {@link #LINK_REFERENCE_COMPONENT_SEPARATOR}.
*
* @param linkReference a link reference, taken from a link XHTML marker / annotation / meta data.
* @return the link protocol extracted from the given link reference
*/
private String getLinkProtocol(String linkReference)
{
int beginIndex = linkReference.indexOf(LINK_REFERENCE_COMPONENT_SEPARATOR);
if (beginIndex < 0) {
return null;
}
beginIndex += LINK_REFERENCE_COMPONENT_SEPARATOR.length();
int endIndex = linkReference.indexOf(LINK_REFERENCE_COMPONENT_SEPARATOR, beginIndex);
if (endIndex < 0) {
return null;
}
return linkReference.substring(beginIndex, endIndex);
}
}