/* * 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.dom.client; import java.util.Iterator; import com.google.gwt.dom.client.Node; /** * Extends the document implementation provided by GWT to add support for selection and range. * <p> * See http://code.google.com/p/google-web-toolkit/issues/detail?id=3006 and * http://code.google.com/p/google-web-toolkit/issues/detail?id=3053. * * @version $Id: 3fbed58218d87348f1e2a451b70680315cf94f2d $ */ public class Document extends com.google.gwt.dom.client.Document { /** * Default constructor. Needs to be protected because all instances are created from JavaScript. */ protected Document() { super(); } /** * @param data contains the data to be added to the comment. * @return the created comment node. */ public final native Node createComment(String data) /*-{ return this.createComment(data); }-*/; /** * Creates an empty document fragment. * <p> * A DocumentFragment is a minimal document object that has no parent. It supports the following DOM 2 methods: * appendChild, cloneNode, hasAttributes, hasChildNodes, insertBefore, normalize, removeChild, replaceChild. * <p> * It also supports the following DOM 2 properties: attributes, childNodes, firstChild, lastChild, localName, * namespaceURI, nextSibling, nodeName, nodeType, nodeValue, ownerDocument, parentNode, prefix, previousSibling, * textContent. * <p> * Various other methods can take a document fragment as an argument (e.g. Node interface methods such as * appendChild and insertBefore), in which case the children of the fragment are appended or inserted, not the * fragment itself. * * @return The newly created document fragment. */ public final native DocumentFragment createDocumentFragment() /*-{ return this.createDocumentFragment(); }-*/; /** * We've added this method because at the time of writing {@link com.google.gwt.dom.client.Document} doesn't offer * support for retrieving the current selection. * * @return The selection object associated with this document. * @see "http://code.google.com/p/google-web-toolkit/issues/detail?id=3053" */ public final Selection getSelection() { return SelectionManager.INSTANCE.getSelection(this); } /** * We've added this method because at the time of writing {@link com.google.gwt.dom.client.Document} doesn't offer * support for creating a range. * <p> * See http://code.google.com/p/google-web-toolkit/issues/detail?id=3053. * * @return A new range for this document. */ public final Range createRange() { return RangeFactory.INSTANCE.createRange(this); } /** * Creates a copy of a node from an external document that can be inserted into this document. * <p> * We've added this method because at time of writing * {@link com.google.gwt.dom.client.Document#importNode(Node, boolean)} is not well implemented. * <p> * See http://code.google.com/p/google-web-toolkit/issues/detail?id=3006. * * @param externalNode The node from another document to be imported. * @param deep Indicates whether the children of the given node need to be imported. * @return a copy of the given node that can be inserted into this document. */ public final Node xImportNode(Node externalNode, boolean deep) { return DOMUtils.getInstance().importNode(this, externalNode, deep); } /** * Returns an iterator for the depth-first pre-order strategy, starting in <code>startNode</code>. * <p> * See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-Document. * * @param startNode node to start iteration from * @return the depth-first pre-order iterator * @see DepthFirstPreOrderIterator */ public final Iterator<Node> getIterator(Node startNode) { return new DepthFirstPreOrderIterator(startNode); } /** * When an HTML document has been switched to designMode, the document object exposes the execCommand method which * allows one to run commands to manipulate the contents of the editable region. Most commands affect the document's * selection (bold, italics, etc), while others insert new elements (adding a link) or affect an entire line * (indenting). When using contentEditable, calling execCommand will affect the currently active editable element. * * @param command The name of the command. * @param parameter Some commands (such as insertimage) require an extra value argument (the image's url). Pass an * argument of null if no argument is needed. * @return true if the specified command has been successfully executed. */ public final native boolean execCommand(String command, String parameter) /*-{ try{ return this.execCommand(command, false, parameter); } catch(e) { return false; } }-*/; /** * @param command The name of the command to query. * @return The current value of the current range for the given command. If a command value has not been explicitly * set then it returns {@code null}. */ public final native String queryCommandValue(String command) /*-{ try{ var value = this.queryCommandValue(command); // Not all commands have a string value (integer and boolean are also possible) so we must make sure that // the returned value is a string, preserving the null value. if (value == null || value == undefined) { return null; } else { return '' + value; } } catch(e) { return null; } }-*/; /** * @param command The name of the command to query. * @return true if the given command can be executed on the current range. */ public final native boolean queryCommandEnabled(String command) /*-{ try{ return this.queryCommandEnabled(command); } catch(e) { return false; } }-*/; /** * @param command The name of the command to query. * @return true if the given command has been executed on the current range. */ public final native boolean queryCommandState(String command) /*-{ try{ return this.queryCommandState(command); } catch(e) { return false; } }-*/; /** * @param command The name of the command to query. * @return true if the given command is supported by the current browser. */ public final native boolean queryCommandSupported(String command) /*-{ try{ return this.queryCommandSupported(command); } catch(e) { return true; } }-*/; /** * Opens a document stream for writing. */ public final native void open() /*-{ this.open(); }-*/; /** * Closes a document stream for writing. */ public final native void close() /*-{ this.close(); }-*/; /** * Writes a string of text to a document stream opened by {@link #open()}. * * @param html a string containing the HTML to be written to the document. */ public final native void write(String html) /*-{ this.write(html); }-*/; /** * Registers a new listener for changes to <code>innerHTML</code> property of element within this document. * * @param listener The listener to be registered. */ public final native void addInnerHTMLListener(InnerHTMLListener listener) /*-{ if (!this.innerHTMLListeners) { this.innerHTMLListeners = []; } this.innerHTMLListeners.push(listener); }-*/; /** * Stop sending notifications to the given listener when the <code>innerHTML</code> property, of some element * included in this document, changes. * * @param listener The listener to be unregistered. */ public final native void removeInnerHTMLListener(InnerHTMLListener listener) /*-{ if (this.innerHTMLListeners) { for (var i = 0; i < this.innerHTMLListeners.length; i++) { if (this.innerHTMLListeners[i] == listener) { this.innerHTMLListeners.splice(i, 1); } } } }-*/; /** * Notify all listeners of the change to the given element's {@code innerHTML} property. * <p> * NOTE: Normally, only {@link Element#xSetInnerHTML(String)} should call this method. * * @param element The element whose <code>innerHTML</code> property has changed. * @see Element#xSetInnerHTML(String) */ public final native void fireInnerHTMLChange(Element element) /*-{ if (this.innerHTMLListeners) { for (var i = 0; i < this.innerHTMLListeners.length; i++) { this.innerHTMLListeners[i]. @org.xwiki.gwt.dom.client.InnerHTMLListener::onInnerHTMLChange(Lorg/xwiki/gwt/dom/client/Element;)(element); } } }-*/; /** * Puts this document in design mode or in view-only mode. * <p> * NOTE: The standard implementation of this method sets the value of the {@code designMode} DOM document property. * We set the value of the {@code contentEditable} property on the document's body instead, if the browser doesn't * fully support the {@code designMode} property. * * @param designMode {@code true} to enter design mode, {@code false} to go back to view-only mode */ public final void setDesignMode(boolean designMode) { DOMUtils.getInstance().setDesignMode(this, designMode); } /** * @return {@code true} if this document is in design mode, {@code false} otherwise * @see #setDesignMode(boolean) */ public final boolean isDesignMode() { return DOMUtils.getInstance().isDesignMode(this); } }