@matthewp/linkedom
Version:
A triple-linked lists based DOM implementation
107 lines (88 loc) • 2.36 kB
JavaScript
;
// https://dom.spec.whatwg.org/#concept-live-range
const {END, NEXT, PREV, START} = require('../shared/symbols.js');
const {getEnd, setAdjacent} = require('../shared/utils.js');
const deleteContents = ({[START]: start, [END]: end}, fragment = null) => {
setAdjacent(start[PREV], end[NEXT]);
do {
const after = getEnd(start);
const next = after === end ? after : after[NEXT];
if (fragment)
fragment.insertBefore(start, fragment[END]);
else
start.remove();
start = next;
} while (start !== end);
};
/**
* @implements globalThis.Range
*/
class Range {
constructor() {
this[START] = null;
this[END] = null;
this.commonAncestorContainer = null;
}
/* TODO: this is more complicated than it looks
setStart(node, offset) {
this[START] = node.childNodes[offset];
}
setEnd(node, offset) {
this[END] = getEnd(node.childNodes[offset]);
}
//*/
insertNode(newNode) {
this[END].parentNode.insertBefore(newNode, this[START]);
}
selectNode(node) {
this[START] = node;
this[END] = getEnd(node);
}
surroundContents(parentNode) {
parentNode.replaceChildren(this.extractContents());
}
setStartBefore(node) {
this[START] = node;
}
setStartAfter(node) {
this[START] = node.nextSibling;
}
setEndBefore(node) {
this[END] = getEnd(node.previousSibling);
}
setEndAfter(node) {
this[END] = getEnd(node);
}
cloneContents() {
let {[START]: start, [END]: end} = this;
const fragment = start.ownerDocument.createDocumentFragment();
while (start !== end) {
fragment.insertBefore(start.cloneNode(true), fragment[END]);
start = getEnd(start);
if (start !== end)
start = start[NEXT];
}
return fragment;
}
deleteContents() {
deleteContents(this);
}
extractContents() {
const fragment = this[START].ownerDocument.createDocumentFragment();
deleteContents(this, fragment);
return fragment;
}
createContextualFragment(html) {
const template = this.commonAncestorContainer.createElement('template');
template.innerHTML = html;
this.selectNode(template.content);
return template.content;
}
cloneRange() {
const range = new Range;
range[START] = this[START];
range[END] = this[END];
return range;
}
}
exports.Range = Range