/* * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Alexandre Russel * * $Id$ */ package org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator; import java.util.ArrayList; import java.util.List; import org.nuxeo.ecm.platform.annotations.gwt.client.AnnotationConstant; import org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController; import org.nuxeo.ecm.platform.annotations.gwt.client.model.Annotation; import org.nuxeo.ecm.platform.annotations.gwt.client.view.listener.AnnotationPopupEventListener; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.SpanElement; import com.google.gwt.dom.client.Text; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; /** * @author Alexandre Russel */ public class AnnoteaDecoratorVisitor implements DecoratorVisitor { private boolean decorating; private boolean previousIsCarriageReturnElement; private static List<String> carriagesReturnedElements = new ArrayList<String>(); static { carriagesReturnedElements.add("div"); carriagesReturnedElements.add("br"); carriagesReturnedElements.add("p"); } private boolean lastCharIsSpace; private final Node startNode; private boolean started; private int offset; private int textToAnnotate; private final Annotation annotation; private final AnnotationController controller; public AnnoteaDecoratorVisitor(Node startNode, int annotatedText, int offset, Annotation annotation, AnnotationController controller) { this.startNode = startNode; this.textToAnnotate = annotatedText; this.offset = offset; this.annotation = annotation; this.controller = controller; } public boolean isLastCharIsSpace() { return lastCharIsSpace; } public void setLastCharIsSpace(boolean lastCharIsSpace) { this.lastCharIsSpace = lastCharIsSpace; } public boolean doBreak() { return textToAnnotate == 0; } public void process(Node node) { if (node.equals(startNode)) { started = true; } else if (started) { if (!decorating) { processToFirstNode(node); } else { processNode(node); } if (carriagesReturnedElements.contains(node.getNodeName().toLowerCase())) { previousIsCarriageReturnElement = true; } else { previousIsCarriageReturnElement = false; } } } private void insertBefore(Node parent, Node child, Node newChild) { if (child == null) { parent.appendChild(newChild); } else { parent.insertBefore(newChild, child); } } private void processNode(Node node) { if (!(node.getNodeType() == Node.TEXT_NODE)) { if (node.getNodeName().equalsIgnoreCase("td")) { textToAnnotate -= 1; } return; } Text text = (Text) node; Node parent = text.getParentNode(); String data = text.getData(); processDecoratedNode(node, data, parent); node.getParentNode().removeChild(node); } public String[] getSelectedText(String rawText, int length) { String text = ""; for (int x = 0; x <= rawText.length(); x++) { text = rawText.substring(0, x); text = removeWhiteSpace(text); if (text.length() == length) { return new String[] { text, rawText.substring(0, x), rawText.substring(x) }; } } return new String[] { text, rawText, "" }; } public String removeWhiteSpace(String data) { data = data.replaceAll("\\s+", " "); boolean startWithSpace = data.startsWith(" "); boolean endWithSpace = data.endsWith(" "); data = data.trim(); if (lastCharIsSpace && !startWithSpace && !previousIsCarriageReturnElement) { data = " " + data; } else if (!lastCharIsSpace && startWithSpace && !previousIsCarriageReturnElement) { data = " " + data; } lastCharIsSpace = endWithSpace; return data; } private SpanElement getSpanElement(Document document) { SpanElement spanElement = document.createSpanElement(); DOM.sinkEvents((Element) spanElement.cast(), Event.ONMOUSEOVER | Event.ONMOUSEOUT); DOM.setEventListener((Element) spanElement.cast(), AnnotationPopupEventListener.getAnnotationPopupEventListener(annotation, controller)); spanElement.setClassName(AnnotationConstant.IGNORED_ELEMENT + " " + controller.getDecorateClassName() + " " + AnnotationConstant.DECORATE_CLASS_NAME + annotation.getId()); return spanElement; } private void processToFirstNode(Node node) { if (!(node.getNodeType() == Node.TEXT_NODE)) { return; } Text text = (Text) node; String data = text.getData(); if (data.length() < offset) { offset -= data.length(); return; } decorating = true; Node parent = text.getParentNode(); if (data.endsWith(" ")) { lastCharIsSpace = true; } String notInData = data.substring(0, offset); text.setData(notInData); processDecoratedNode(node, data.substring(offset), parent); } private void processDecoratedNode(Node node, String data, Node parent) { String[] selectedText = getSelectedText(data, textToAnnotate); if (selectedText[1].trim().length() == 0 && selectedText[2].trim().length() == 0 && node.getParentNode().getNodeName().equalsIgnoreCase("tr")) { // don't add nodes to tr textToAnnotate -= selectedText[0].length(); return; } Document document = node.getOwnerDocument(); SpanElement spanElement = getSpanElement(document); spanElement.setInnerText(selectedText[1]); insertBefore(parent, node.getNextSibling(), spanElement); if (selectedText[2].length() > 0) { insertBefore(parent, spanElement.getNextSibling(), document.createTextNode(selectedText[2])); } textToAnnotate -= selectedText[0].length(); } }