com.gargoylesoftware.htmlunit.javascript.host.Range Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-client-compiler-deps Show documentation
Show all versions of vaadin-client-compiler-deps Show documentation
Vaadin is a web application framework for Rich Internet Applications (RIA).
Vaadin enables easy development and maintenance of fast and
secure rich web
applications with a stunning look and feel and a wide browser support.
It features a server-side architecture with the majority of the logic
running
on the server. Ajax technology is used at the browser-side to ensure a
rich
and interactive user experience.
/*
* Copyright (c) 2002-2011 Gargoyle Software Inc.
*
* 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 com.gargoylesoftware.htmlunit.javascript.host;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.logging.LogFactory;
import com.gargoylesoftware.htmlunit.SgmlPage;
import com.gargoylesoftware.htmlunit.html.DomDocumentFragment;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.HTMLParser;
import com.gargoylesoftware.htmlunit.html.impl.SimpleRange;
import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLDocument;
/**
* The JavaScript object that represents a Range.
*
* @see XULPlanet
* @see
* DOM-Level-2-Traversal-Range
* @version $Revision: 6392 $
* @author Marc Guillemot
* @author Ahmed Ashour
* @author Daniel Gredler
*/
public class Range extends SimpleScriptable {
private Node startContainer_, endContainer_;
private int startOffset_, endOffset_;
/** Comparison mode for compareBoundaryPoints. */
public static final short START_TO_START = 0;
/** Comparison mode for compareBoundaryPoints. */
public static final short START_TO_END = 1;
/** Comparison mode for compareBoundaryPoints. */
public static final short END_TO_END = 2;
/** Comparison mode for compareBoundaryPoints. */
public static final short END_TO_START = 3;
/**
* Creates a new instance.
*/
public Range() {
// Empty.
}
/**
* Creates a new instance.
* @param document the HTML document creating the range
*/
public Range(final HTMLDocument document) {
startContainer_ = document;
endContainer_ = document;
}
Range(final org.w3c.dom.ranges.Range w3cRange) {
final DomNode domNodeStartContainer = (DomNode) w3cRange.getStartContainer();
startContainer_ = (Node) (domNodeStartContainer).getScriptObject();
startOffset_ = w3cRange.getStartOffset();
final DomNode domNodeEndContainer = (DomNode) w3cRange.getEndContainer();
endContainer_ = (Node) (domNodeEndContainer).getScriptObject();
endOffset_ = w3cRange.getEndOffset();
}
/**
* {@inheritDoc}
*/
@Override
public Object getDefaultValue(final Class< ? > hint) {
return toW3C().toString();
}
/**
* Gets the node within which the Range begins.
* @return undefined
if not initialized
*/
public Object jsxGet_startContainer() {
if (startContainer_ == null) {
return Context.getUndefinedValue();
}
return startContainer_;
}
/**
* Gets the node within which the Range ends.
* @return undefined
if not initialized
*/
public Object jsxGet_endContainer() {
if (endContainer_ == null) {
return Context.getUndefinedValue();
}
return endContainer_;
}
/**
* Gets the offset within the starting node of the Range.
* @return 0
if not initialized
*/
public int jsxGet_startOffset() {
return startOffset_;
}
/**
* Gets the offset within the end node of the Range.
* @return 0
if not initialized
*/
public int jsxGet_endOffset() {
return endOffset_;
}
/**
* Sets the attributes describing the start of a Range.
* @param refNode the reference node
* @param offset the offset value within the node
*/
public void jsxFunction_setStart(final Node refNode, final int offset) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setStart() with a null node.");
}
startContainer_ = refNode;
startOffset_ = offset;
}
/**
* Sets the start of the range to be after the node.
* @param refNode the reference node
*/
public void jsxFunction_setStartAfter(final Node refNode) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setStartAfter() with a null node.");
}
startContainer_ = refNode.getParent();
startOffset_ = getPositionInContainer(refNode) + 1;
}
/**
* Sets the start of the range to be before the node.
* @param refNode the reference node
*/
public void jsxFunction_setStartBefore(final Node refNode) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setStartBefore() with a null node.");
}
startContainer_ = refNode.getParent();
startOffset_ = getPositionInContainer(refNode);
}
private int getPositionInContainer(final Node refNode) {
int i = 0;
Node node = refNode;
while (node.jsxGet_previousSibling() != null) {
node = node.jsxGet_previousSibling();
++i;
}
return i;
}
/**
* Indicates if the range is collapsed.
* @return true
if the range is collapsed
*/
public boolean jsxGet_collapsed() {
return (startContainer_ == endContainer_ && startOffset_ == endOffset_);
}
/**
* Sets the attributes describing the end of a Range.
* @param refNode the reference node
* @param offset the offset value within the node
*/
public void jsxFunction_setEnd(final Node refNode, final int offset) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setEnd() with a null node.");
}
endContainer_ = refNode;
endOffset_ = offset;
}
/**
* Sets the end of the range to be after the node.
* @param refNode the reference node
*/
public void jsxFunction_setEndAfter(final Node refNode) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setEndAfter() with a null node.");
}
endContainer_ = refNode.getParent();
endOffset_ = getPositionInContainer(refNode) + 1;
}
/**
* Sets the end of the range to be before the node.
* @param refNode the reference node
*/
public void jsxFunction_setEndBefore(final Node refNode) {
if (refNode == null) {
throw Context.reportRuntimeError("It is illegal to call Range.setEndBefore() with a null node.");
}
startContainer_ = refNode.getParent();
startOffset_ = getPositionInContainer(refNode);
}
/**
* Select the contents within a node.
* @param refNode Node to select from
*/
public void jsxFunction_selectNodeContents(final Node refNode) {
startContainer_ = refNode;
startOffset_ = 0;
endContainer_ = refNode;
endOffset_ = refNode.jsxGet_childNodes().jsxGet_length();
}
/**
* Selects a node and its contents.
* @param refNode the node to select
*/
public void jsxFunction_selectNode(final Node refNode) {
jsxFunction_setStartBefore(refNode);
jsxFunction_setEndAfter(refNode);
}
/**
* Collapse a Range onto one of its boundaries.
* @param toStart if true
, collapses the Range onto its start; else collapses it onto its end
*/
public void jsxFunction_collapse(final boolean toStart) {
if (toStart) {
endContainer_ = startContainer_;
endOffset_ = startOffset_;
}
else {
startContainer_ = endContainer_;
startOffset_ = endOffset_;
}
}
/**
* Returns the deepest common ancestor container of the Range's two boundary points.
* @return the deepest common ancestor container of the Range's two boundary points
*/
public Object jsxGet_commonAncestorContainer() {
final Node ancestor = getCommonAncestor();
if (ancestor == null) {
return Context.getUndefinedValue();
}
return ancestor;
}
/**
* Returns the deepest common ancestor container of the Range's two boundary points.
* @return the deepest common ancestor container of the Range's two boundary points
*/
@SuppressWarnings("unchecked")
private Node getCommonAncestor() {
final List startAncestors = getAncestorsAndSelf(startContainer_);
final List endAncestors = getAncestorsAndSelf(endContainer_);
final List commonAncestors = ListUtils.intersection(startAncestors, endAncestors);
if (commonAncestors.isEmpty()) {
return null;
}
return commonAncestors.get(commonAncestors.size() - 1);
}
/**
* Returns the ancestors of the specified node.
* @param node the node to start with
* @return the ancestors of the specified node
*/
private List getAncestorsAndSelf(final Node node) {
final List ancestors = new ArrayList();
Node ancestor = node;
while (ancestor != null) {
ancestors.add(0, ancestor);
ancestor = ancestor.getParent();
}
return ancestors;
}
/**
* Parses an HTML snippet.
* @param valueAsString text that contains text and tags to be converted to a document fragment
* @return a document fragment
* @see Mozilla documentation
*/
public Object jsxFunction_createContextualFragment(final String valueAsString) {
final SgmlPage page = startContainer_.getDomNodeOrDie().getPage();
final DomDocumentFragment fragment = new DomDocumentFragment(page);
try {
HTMLParser.parseFragment(fragment, startContainer_.getDomNodeOrDie(), valueAsString);
}
catch (final Exception e) {
LogFactory.getLog(Range.class).error("Unexpected exception occurred in createContextualFragment", e);
throw Context.reportRuntimeError("Unexpected exception occurred in createContextualFragment: "
+ e.getMessage());
}
return fragment.getScriptObject();
}
/**
* Moves this range's contents from the document tree into a document fragment.
* @return the new document fragment containing the range contents
*/
public Object jsxFunction_extractContents() {
return toW3C().extractContents().getScriptObject();
}
/**
* Returns a W3C {@link org.w3c.dom.ranges.Range} version of this object.
* @return a W3C {@link org.w3c.dom.ranges.Range} version of this object
*/
public SimpleRange toW3C() {
return new SimpleRange(startContainer_.getDomNodeOrNull(), startOffset_,
endContainer_.getDomNodeOrDie(), endOffset_);
}
/**
* Compares the boundary points of two Ranges.
* @param how a constant describing the comparison method
* @param sourceRange the Range to compare boundary points with this range
* @return -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before,
* equal to, or after the corresponding boundary-point of sourceRange.
*/
public Object jsxFunction_compareBoundaryPoints(final int how, final Range sourceRange) {
final Node nodeForThis;
final int offsetForThis;
final int containingMoficator;
if (START_TO_START == how || END_TO_START == how) {
nodeForThis = startContainer_;
offsetForThis = startOffset_;
containingMoficator = 1;
}
else {
nodeForThis = endContainer_;
offsetForThis = endOffset_;
containingMoficator = -1;
}
final Node nodeForOther;
final int offsetForOther;
if (START_TO_END == how || START_TO_START == how) {
nodeForOther = sourceRange.startContainer_;
offsetForOther = sourceRange.startOffset_;
}
else {
nodeForOther = sourceRange.endContainer_;
offsetForOther = sourceRange.endOffset_;
}
if (nodeForThis == nodeForOther) {
if (offsetForThis < offsetForOther) {
return Integer.valueOf(-1);
}
else if (offsetForThis < offsetForOther) {
return Integer.valueOf(1);
}
return Integer.valueOf(0);
}
final byte nodeComparision = (byte) nodeForThis.jsxFunction_compareDocumentPosition(nodeForOther);
if ((nodeComparision & Node.DOCUMENT_POSITION_CONTAINED_BY) != 0) {
return Integer.valueOf(-1 * containingMoficator);
}
else if ((nodeComparision & Node.DOCUMENT_POSITION_PRECEDING) != 0) {
return Integer.valueOf(-1);
}
// TODO: handle other cases!
return Integer.valueOf(1);
}
}