jsdom
Version:
A JavaScript implementation of many web standards
898 lines (721 loc) • 26.9 kB
JavaScript
"use strict";
const DOMException = require("../generated/DOMException");
const { clone } = require("../node");
const NODE_TYPE = require("../node-type");
const { parseFragment } = require("../../browser/parser/index");
const { HTML_NS } = require("../helpers/namespaces");
const { domSymbolTree } = require("../helpers/internal-constants");
const { compareBoundaryPointsPosition } = require("./boundary-point");
const { nodeRoot, nodeLength, isInclusiveAncestor } = require("../helpers/node");
const { createElement } = require("../helpers/create-element");
const AbstractRangeImpl = require("./AbstractRange-impl").implementation;
const Range = require("../generated/Range");
const DocumentFragment = require("../generated/DocumentFragment");
const { implForWrapper } = require("../generated/utils");
const RANGE_COMPARISON_TYPE = {
START_TO_START: 0,
START_TO_END: 1,
END_TO_END: 2,
END_TO_START: 3
};
class RangeImpl extends AbstractRangeImpl {
constructor(globalObject, args, privateData) {
super(globalObject, args, privateData);
const defaultBoundaryPoint = {
node: implForWrapper(globalObject._document),
offset: 0
};
const {
start = defaultBoundaryPoint,
end = defaultBoundaryPoint
} = privateData;
this._setLiveRangeStart(start.node, start.offset);
this._setLiveRangeEnd(end.node, end.offset);
}
// https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer
get commonAncestorContainer() {
const { _start, _end } = this;
for (const container of domSymbolTree.ancestorsIterator(_start.node)) {
if (isInclusiveAncestor(container, _end.node)) {
return container;
}
}
return null;
}
// https://dom.spec.whatwg.org/#dom-range-setstart
setStart(node, offset) {
setBoundaryPointStart(this, node, offset);
}
// https://dom.spec.whatwg.org/#dom-range-setend
setEnd(node, offset) {
setBoundaryPointEnd(this, node, offset);
}
// https://dom.spec.whatwg.org/#dom-range-setstartbefore
setStartBefore(node) {
const parent = domSymbolTree.parent(node);
if (!parent) {
throw DOMException.create(this._globalObject, ["The given Node has no parent.", "InvalidNodeTypeError"]);
}
setBoundaryPointStart(this, parent, domSymbolTree.index(node));
}
// https://dom.spec.whatwg.org/#dom-range-setstartafter
setStartAfter(node) {
const parent = domSymbolTree.parent(node);
if (!parent) {
throw DOMException.create(this._globalObject, ["The given Node has no parent.", "InvalidNodeTypeError"]);
}
setBoundaryPointStart(this, parent, domSymbolTree.index(node) + 1);
}
// https://dom.spec.whatwg.org/#dom-range-setendbefore
setEndBefore(node) {
const parent = domSymbolTree.parent(node);
if (!parent) {
throw DOMException.create(this._globalObject, ["The given Node has no parent.", "InvalidNodeTypeError"]);
}
setBoundaryPointEnd(this, parent, domSymbolTree.index(node));
}
// https://dom.spec.whatwg.org/#dom-range-setendafter
setEndAfter(node) {
const parent = domSymbolTree.parent(node);
if (!parent) {
throw DOMException.create(this._globalObject, ["The given Node has no parent.", "InvalidNodeTypeError"]);
}
setBoundaryPointEnd(this, parent, domSymbolTree.index(node) + 1);
}
// https://dom.spec.whatwg.org/#dom-range-collapse
collapse(toStart) {
if (toStart) {
this._setLiveRangeEnd(this._start.node, this._start.offset);
} else {
this._setLiveRangeStart(this._end.node, this._end.offset);
}
}
// https://dom.spec.whatwg.org/#dom-range-selectnode
selectNode(node) {
selectNodeWithinRange(node, this);
}
// https://dom.spec.whatwg.org/#dom-range-selectnodecontents
selectNodeContents(node) {
if (node.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE) {
throw DOMException.create(this._globalObject, [
"DocumentType Node can't be used as boundary point.",
"InvalidNodeTypeError"
]);
}
const length = nodeLength(node);
this._setLiveRangeStart(node, 0);
this._setLiveRangeEnd(node, length);
}
// https://dom.spec.whatwg.org/#dom-range-compareboundarypoints
compareBoundaryPoints(how, sourceRange) {
if (
how !== RANGE_COMPARISON_TYPE.START_TO_START &&
how !== RANGE_COMPARISON_TYPE.START_TO_END &&
how !== RANGE_COMPARISON_TYPE.END_TO_END &&
how !== RANGE_COMPARISON_TYPE.END_TO_START
) {
const message = "The comparison method provided must be one of 'START_TO_START', 'START_TO_END', 'END_TO_END', " +
"or 'END_TO_START'.";
throw DOMException.create(this._globalObject, [message, "NotSupportedError"]);
}
if (this._root !== sourceRange._root) {
throw DOMException.create(this._globalObject, ["The two Ranges are not in the same tree.", "WrongDocumentError"]);
}
let thisPoint, otherPoint;
if (how === RANGE_COMPARISON_TYPE.START_TO_START) {
thisPoint = this._start;
otherPoint = sourceRange._start;
} else if (how === RANGE_COMPARISON_TYPE.START_TO_END) {
thisPoint = this._end;
otherPoint = sourceRange._start;
} else if (how === RANGE_COMPARISON_TYPE.END_TO_END) {
thisPoint = this._end;
otherPoint = sourceRange._end;
} else {
thisPoint = this._start;
otherPoint = sourceRange._end;
}
return compareBoundaryPointsPosition(thisPoint, otherPoint);
}
// https://dom.spec.whatwg.org/#dom-range-deletecontents
deleteContents() {
if (this.collapsed) {
return;
}
const { _start: originalStart, _end: originalEnd } = this;
if (
originalStart.node === originalEnd.node &&
(
originalStart.node.nodeType === NODE_TYPE.TEXT_NODE ||
originalStart.node.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
originalStart.node.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
originalStart.node.replaceData(originalStart.offset, originalEnd.offset - originalStart.offset, "");
return;
}
const nodesToRemove = [];
let currentNode = this._start.node;
const endNode = nextNodeDescendant(this._end.node);
while (currentNode && currentNode !== endNode) {
if (
isContained(currentNode, this) &&
!isContained(domSymbolTree.parent(currentNode), this)
) {
nodesToRemove.push(currentNode);
}
currentNode = domSymbolTree.following(currentNode);
}
let newNode, newOffset;
if (isInclusiveAncestor(originalStart.node, originalEnd.node)) {
newNode = originalStart.node;
newOffset = originalStart.offset;
} else {
let referenceNode = originalStart.node;
while (
referenceNode &&
!isInclusiveAncestor(domSymbolTree.parent(referenceNode), originalEnd.node)
) {
referenceNode = domSymbolTree.parent(referenceNode);
}
newNode = domSymbolTree.parent(referenceNode);
newOffset = domSymbolTree.index(referenceNode) + 1;
}
if (
originalStart.node.nodeType === NODE_TYPE.TEXT_NODE ||
originalStart.node.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
originalStart.node.nodeType === NODE_TYPE.COMMENT_NODE
) {
originalStart.node.replaceData(originalStart.offset, nodeLength(originalStart.node) - originalStart.offset, "");
}
for (const node of nodesToRemove) {
const parent = domSymbolTree.parent(node);
parent.removeChild(node);
}
if (
originalEnd.node.nodeType === NODE_TYPE.TEXT_NODE ||
originalEnd.node.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
originalEnd.node.nodeType === NODE_TYPE.COMMENT_NODE
) {
originalEnd.node.replaceData(0, originalEnd.offset, "");
}
this._setLiveRangeStart(newNode, newOffset);
this._setLiveRangeEnd(newNode, newOffset);
}
// https://dom.spec.whatwg.org/#dom-range-extractcontents
extractContents() {
return extractRange(this);
}
// https://dom.spec.whatwg.org/#dom-range-clonecontents
cloneContents() {
return cloneRange(this);
}
// https://dom.spec.whatwg.org/#dom-range-insertnode
insertNode(node) {
insertNodeInRange(node, this);
}
// https://dom.spec.whatwg.org/#dom-range-surroundcontents
surroundContents(newParent) {
let node = this.commonAncestorContainer;
const endNode = nextNodeDescendant(node);
while (node !== endNode) {
if (node.nodeType !== NODE_TYPE.TEXT_NODE && isPartiallyContained(node, this)) {
throw DOMException.create(this._globalObject, [
"The Range has partially contains a non-Text node.",
"InvalidStateError"
]);
}
node = domSymbolTree.following(node);
}
if (
newParent.nodeType === NODE_TYPE.DOCUMENT_NODE ||
newParent.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE ||
newParent.nodeType === NODE_TYPE.DOCUMENT_FRAGMENT_NODE
) {
throw DOMException.create(this._globalObject, ["Invalid element type.", "InvalidNodeTypeError"]);
}
const fragment = extractRange(this);
while (domSymbolTree.firstChild(newParent)) {
newParent.removeChild(domSymbolTree.firstChild(newParent));
}
insertNodeInRange(newParent, this);
newParent.appendChild(fragment);
selectNodeWithinRange(newParent, this);
}
// https://dom.spec.whatwg.org/#dom-range-clonerange
cloneRange() {
const { _start, _end, _globalObject } = this;
return Range.createImpl(_globalObject, [], {
start: { node: _start.node, offset: _start.offset },
end: { node: _end.node, offset: _end.offset }
});
}
// https://dom.spec.whatwg.org/#dom-range-detach
detach() {
// Do nothing by spec!
}
// https://dom.spec.whatwg.org/#dom-range-ispointinrange
isPointInRange(node, offset) {
if (nodeRoot(node) !== this._root) {
return false;
}
validateSetBoundaryPoint(node, offset);
const bp = { node, offset };
if (
compareBoundaryPointsPosition(bp, this._start) === -1 ||
compareBoundaryPointsPosition(bp, this._end) === 1
) {
return false;
}
return true;
}
// https://dom.spec.whatwg.org/#dom-range-comparepoint
comparePoint(node, offset) {
if (nodeRoot(node) !== this._root) {
throw DOMException.create(this._globalObject, [
"The given Node and the Range are not in the same tree.",
"WrongDocumentError"
]);
}
validateSetBoundaryPoint(node, offset);
const bp = { node, offset };
if (compareBoundaryPointsPosition(bp, this._start) === -1) {
return -1;
} else if (compareBoundaryPointsPosition(bp, this._end) === 1) {
return 1;
}
return 0;
}
// https://dom.spec.whatwg.org/#dom-range-intersectsnode
intersectsNode(node) {
if (nodeRoot(node) !== this._root) {
return false;
}
const parent = domSymbolTree.parent(node);
if (!parent) {
return true;
}
const offset = domSymbolTree.index(node);
return (
compareBoundaryPointsPosition({ node: parent, offset }, this._end) === -1 &&
compareBoundaryPointsPosition({ node: parent, offset: offset + 1 }, this._start) === 1
);
}
// https://dom.spec.whatwg.org/#dom-range-stringifier
toString() {
let s = "";
const { _start, _end } = this;
if (_start.node === _end.node && _start.node.nodeType === NODE_TYPE.TEXT_NODE) {
return _start.node.data.slice(_start.offset, _end.offset);
}
if (_start.node.nodeType === NODE_TYPE.TEXT_NODE) {
s += _start.node.data.slice(_start.offset);
}
let currentNode = _start.node;
const endNode = nextNodeDescendant(_end.node);
while (currentNode && currentNode !== endNode) {
if (currentNode.nodeType === NODE_TYPE.TEXT_NODE && isContained(currentNode, this)) {
s += currentNode.data;
}
currentNode = domSymbolTree.following(currentNode);
}
if (_end.node.nodeType === NODE_TYPE.TEXT_NODE) {
s += _end.node.data.slice(0, _end.offset);
}
return s;
}
// https://w3c.github.io/DOM-Parsing/#dom-range-createcontextualfragment
createContextualFragment(fragment) {
const { node } = this._start;
let element;
switch (node.nodeType) {
case NODE_TYPE.DOCUMENT_NODE:
case NODE_TYPE.DOCUMENT_FRAGMENT_NODE:
element = null;
break;
case NODE_TYPE.ELEMENT_NODE:
element = node;
break;
case NODE_TYPE.TEXT_NODE:
case NODE_TYPE.COMMENT_NODE:
element = node.parentElement;
break;
default:
throw new Error("Internal error: Invalid range start node");
}
if (
element === null || (
element._ownerDocument._parsingMode === "html" &&
element._localName === "html" &&
element._namespaceURI === HTML_NS
)
) {
element = createElement(node._ownerDocument, "body", HTML_NS);
}
return parseFragment(fragment, element);
}
// https://dom.spec.whatwg.org/#concept-range-root
get _root() {
return nodeRoot(this._start.node);
}
_setLiveRangeStart(node, offset) {
if (
this._start &&
this._start.node !== node &&
this._start.node !== this._end.node
) {
this._start.node._referencedRanges.delete(this);
}
if (!node._referencedRanges.has(this)) {
node._referencedRanges.add(this);
}
this._start = {
node,
offset
};
}
_setLiveRangeEnd(node, offset) {
if (
this._end &&
this._end.node !== node &&
this._end.node !== this._start.node
) {
this._end.node._referencedRanges.delete(this);
}
if (!node._referencedRanges.has(this)) {
node._referencedRanges.add(this);
}
this._end = {
node,
offset
};
}
}
function nextNodeDescendant(node) {
while (node && !domSymbolTree.nextSibling(node)) {
node = domSymbolTree.parent(node);
}
if (!node) {
return null;
}
return domSymbolTree.nextSibling(node);
}
// https://dom.spec.whatwg.org/#concept-range-bp-set
function validateSetBoundaryPoint(node, offset) {
if (node.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE) {
throw DOMException.create(node._globalObject, [
"DocumentType Node can't be used as boundary point.",
"InvalidNodeTypeError"
]);
}
if (offset > nodeLength(node)) {
throw DOMException.create(node._globalObject, ["Offset out of bound.", "IndexSizeError"]);
}
}
function setBoundaryPointStart(range, node, offset) {
validateSetBoundaryPoint(node, offset);
const bp = { node, offset };
if (
nodeRoot(node) !== range._root ||
compareBoundaryPointsPosition(bp, range._end) === 1
) {
range._setLiveRangeEnd(node, offset);
}
range._setLiveRangeStart(node, offset);
}
function setBoundaryPointEnd(range, node, offset) {
validateSetBoundaryPoint(node, offset);
const bp = { node, offset };
if (
nodeRoot(node) !== range._root ||
compareBoundaryPointsPosition(bp, range._start) === -1
) {
range._setLiveRangeStart(node, offset);
}
range._setLiveRangeEnd(node, offset);
}
// https://dom.spec.whatwg.org/#concept-range-select
function selectNodeWithinRange(node, range) {
const parent = domSymbolTree.parent(node);
if (!parent) {
throw DOMException.create(node._globalObject, ["The given Node has no parent.", "InvalidNodeTypeError"]);
}
const index = domSymbolTree.index(node);
range._setLiveRangeStart(parent, index);
range._setLiveRangeEnd(parent, index + 1);
}
// https://dom.spec.whatwg.org/#contained
function isContained(node, range) {
const { _start, _end } = range;
return (
compareBoundaryPointsPosition({ node, offset: 0 }, _start) === 1 &&
compareBoundaryPointsPosition({ node, offset: nodeLength(node) }, _end) === -1
);
}
// https://dom.spec.whatwg.org/#partially-contained
function isPartiallyContained(node, range) {
const { _start, _end } = range;
return (
(isInclusiveAncestor(node, _start.node) && !isInclusiveAncestor(node, _end.node)) ||
(!isInclusiveAncestor(node, _start.node) && isInclusiveAncestor(node, _end.node))
);
}
// https://dom.spec.whatwg.org/#concept-range-insert
function insertNodeInRange(node, range) {
const { node: startNode, offset: startOffset } = range._start;
if (
startNode.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
startNode.nodeType === NODE_TYPE.COMMENT_NODE ||
(startNode.nodeType === NODE_TYPE.TEXT_NODE && !domSymbolTree.parent(startNode)) ||
node === startNode
) {
throw DOMException.create(node._globalObject, ["Invalid start node.", "HierarchyRequestError"]);
}
let referenceNode = startNode.nodeType === NODE_TYPE.TEXT_NODE ?
startNode :
domSymbolTree.childrenToArray(startNode)[startOffset] || null;
const parent = !referenceNode ?
startNode :
domSymbolTree.parent(referenceNode);
parent._preInsertValidity(node, referenceNode);
if (startNode.nodeType === NODE_TYPE.TEXT_NODE) {
referenceNode = startNode.splitText(startOffset);
}
if (node === referenceNode) {
referenceNode = domSymbolTree.nextSibling(referenceNode);
}
const nodeParent = domSymbolTree.parent(node);
if (nodeParent) {
nodeParent.removeChild(node);
}
let newOffset = !referenceNode ? nodeLength(parent) : domSymbolTree.index(referenceNode);
newOffset += node.nodeType === NODE_TYPE.DOCUMENT_FRAGMENT_NODE ? nodeLength(node) : 1;
parent.insertBefore(node, referenceNode);
if (range.collapsed) {
range._setLiveRangeEnd(parent, newOffset);
}
}
// https://dom.spec.whatwg.org/#concept-range-clone
function cloneRange(range) {
const { _start: originalStart, _end: originalEnd, _globalObject } = range;
const fragment = DocumentFragment.createImpl(_globalObject, [], {
ownerDocument: originalStart.node._ownerDocument
});
if (range.collapsed) {
return fragment;
}
if (
originalStart.node === originalEnd.node &&
(
originalStart.node.nodeType === NODE_TYPE.TEXT_NODE ||
originalStart.node.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
originalStart.node.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalStart.node);
cloned._data = cloned.substringData(originalStart.offset, originalEnd.offset - originalStart.offset);
fragment.appendChild(cloned);
return fragment;
}
let commonAncestor = originalStart.node;
while (!isInclusiveAncestor(commonAncestor, originalEnd.node)) {
commonAncestor = domSymbolTree.parent(commonAncestor);
}
let firstPartialContainedChild = null;
if (!isInclusiveAncestor(originalStart.node, originalEnd.node)) {
let candidate = domSymbolTree.firstChild(commonAncestor);
while (!firstPartialContainedChild) {
if (isPartiallyContained(candidate, range)) {
firstPartialContainedChild = candidate;
}
candidate = domSymbolTree.nextSibling(candidate);
}
}
let lastPartiallyContainedChild = null;
if (!isInclusiveAncestor(originalEnd.node, originalStart.node)) {
let candidate = domSymbolTree.lastChild(commonAncestor);
while (!lastPartiallyContainedChild) {
if (isPartiallyContained(candidate, range)) {
lastPartiallyContainedChild = candidate;
}
candidate = domSymbolTree.previousSibling(candidate);
}
}
const containedChildren = domSymbolTree.childrenToArray(commonAncestor)
.filter(node => isContained(node, range));
const hasDoctypeChildren = containedChildren.some(node => node.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE);
if (hasDoctypeChildren) {
throw DOMException.create(range._globalObject, ["Invalid document type element.", "HierarchyRequestError"]);
}
if (
firstPartialContainedChild !== null &&
(
firstPartialContainedChild.nodeType === NODE_TYPE.TEXT_NODE ||
firstPartialContainedChild.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
firstPartialContainedChild.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalStart.node);
cloned._data = cloned.substringData(originalStart.offset, nodeLength(originalStart.node) - originalStart.offset);
fragment.appendChild(cloned);
} else if (firstPartialContainedChild !== null) {
const cloned = clone(firstPartialContainedChild);
fragment.appendChild(cloned);
const subrange = Range.createImpl(_globalObject, [], {
start: { node: originalStart.node, offset: originalStart.offset },
end: { node: firstPartialContainedChild, offset: nodeLength(firstPartialContainedChild) }
});
const subfragment = cloneRange(subrange);
cloned.appendChild(subfragment);
}
for (const containedChild of containedChildren) {
const cloned = clone(containedChild, undefined, true);
fragment.appendChild(cloned);
}
if (
lastPartiallyContainedChild !== null &&
(
lastPartiallyContainedChild.nodeType === NODE_TYPE.TEXT_NODE ||
lastPartiallyContainedChild.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
lastPartiallyContainedChild.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalEnd.node);
cloned._data = cloned.substringData(0, originalEnd.offset);
fragment.appendChild(cloned);
} else if (lastPartiallyContainedChild !== null) {
const cloned = clone(lastPartiallyContainedChild);
fragment.appendChild(cloned);
const subrange = Range.createImpl(_globalObject, [], {
start: { node: lastPartiallyContainedChild, offset: 0 },
end: { node: originalEnd.node, offset: originalEnd.offset }
});
const subfragment = cloneRange(subrange);
cloned.appendChild(subfragment);
}
return fragment;
}
// https://dom.spec.whatwg.org/#concept-range-extract
function extractRange(range) {
const { _start: originalStart, _end: originalEnd, _globalObject } = range;
const fragment = DocumentFragment.createImpl(_globalObject, [], {
ownerDocument: originalStart.node._ownerDocument
});
if (range.collapsed) {
return fragment;
}
if (
originalStart.node === originalEnd.node &&
(
originalStart.node.nodeType === NODE_TYPE.TEXT_NODE ||
originalStart.node.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
originalStart.node.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalStart.node);
cloned._data = cloned.substringData(originalStart.offset, originalEnd.offset - originalStart.offset);
fragment.appendChild(cloned);
originalStart.node.replaceData(originalStart.offset, originalEnd.offset - originalStart.offset, "");
return fragment;
}
let commonAncestor = originalStart.node;
while (!isInclusiveAncestor(commonAncestor, originalEnd.node)) {
commonAncestor = domSymbolTree.parent(commonAncestor);
}
let firstPartialContainedChild = null;
if (!isInclusiveAncestor(originalStart.node, originalEnd.node)) {
let candidate = domSymbolTree.firstChild(commonAncestor);
while (!firstPartialContainedChild) {
if (isPartiallyContained(candidate, range)) {
firstPartialContainedChild = candidate;
}
candidate = domSymbolTree.nextSibling(candidate);
}
}
let lastPartiallyContainedChild = null;
if (!isInclusiveAncestor(originalEnd.node, originalStart.node)) {
let candidate = domSymbolTree.lastChild(commonAncestor);
while (!lastPartiallyContainedChild) {
if (isPartiallyContained(candidate, range)) {
lastPartiallyContainedChild = candidate;
}
candidate = domSymbolTree.previousSibling(candidate);
}
}
const containedChildren = domSymbolTree.childrenToArray(commonAncestor)
.filter(node => isContained(node, range));
const hasDoctypeChildren = containedChildren.some(node => node.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE);
if (hasDoctypeChildren) {
throw DOMException.create(range._globalObject, ["Invalid document type element.", "HierarchyRequestError"]);
}
let newNode, newOffset;
if (isInclusiveAncestor(originalStart.node, originalEnd.node)) {
newNode = originalStart.node;
newOffset = originalStart.offset;
} else {
let referenceNode = originalStart.node;
while (
referenceNode &&
!isInclusiveAncestor(domSymbolTree.parent(referenceNode), originalEnd.node)
) {
referenceNode = domSymbolTree.parent(referenceNode);
}
newNode = domSymbolTree.parent(referenceNode);
newOffset = domSymbolTree.index(referenceNode) + 1;
}
if (
firstPartialContainedChild !== null &&
(
firstPartialContainedChild.nodeType === NODE_TYPE.TEXT_NODE ||
firstPartialContainedChild.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
firstPartialContainedChild.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalStart.node);
cloned._data = cloned.substringData(originalStart.offset, nodeLength(originalStart.node) - originalStart.offset);
fragment.appendChild(cloned);
originalStart.node.replaceData(originalStart.offset, nodeLength(originalStart.node) - originalStart.offset, "");
} else if (firstPartialContainedChild !== null) {
const cloned = clone(firstPartialContainedChild);
fragment.appendChild(cloned);
const subrange = Range.createImpl(_globalObject, [], {
start: { node: originalStart.node, offset: originalStart.offset },
end: { node: firstPartialContainedChild, offset: nodeLength(firstPartialContainedChild) }
});
const subfragment = extractRange(subrange);
cloned.appendChild(subfragment);
}
for (const containedChild of containedChildren) {
fragment.appendChild(containedChild);
}
if (
lastPartiallyContainedChild !== null &&
(
lastPartiallyContainedChild.nodeType === NODE_TYPE.TEXT_NODE ||
lastPartiallyContainedChild.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE ||
lastPartiallyContainedChild.nodeType === NODE_TYPE.COMMENT_NODE
)
) {
const cloned = clone(originalEnd.node);
cloned._data = cloned.substringData(0, originalEnd.offset);
fragment.appendChild(cloned);
originalEnd.node.replaceData(0, originalEnd.offset, "");
} else if (lastPartiallyContainedChild !== null) {
const cloned = clone(lastPartiallyContainedChild);
fragment.appendChild(cloned);
const subrange = Range.createImpl(_globalObject, [], {
start: { node: lastPartiallyContainedChild, offset: 0 },
end: { node: originalEnd.node, offset: originalEnd.offset }
});
const subfragment = extractRange(subrange);
cloned.appendChild(subfragment);
}
range._setLiveRangeStart(newNode, newOffset);
range._setLiveRangeEnd(newNode, newOffset);
return fragment;
}
module.exports = {
implementation: RangeImpl,
setBoundaryPointStart,
setBoundaryPointEnd
};