Anki/ts/domlib/surround/child-node-range.ts
Henrik Giesel 30bbbaf00b
Use eslint for sorting our imports (#1637)
* Make eslint sort our imports

* fix missing deps in eslint rule (dae)

Caught on Linux due to the stricter sandboxing

* Remove exports-last eslint rule (for now?)

* Adjust browserslist settings

- We use ResizeObserver which is not supported in browsers like KaiOS,
  Baidu or Android UC

* Raise minimum iOS version 13.4

- It's the first version that supports ResizeObserver

* Apply new eslint rules to sort imports
2022-02-04 18:36:34 +10:00

111 lines
2.8 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { elementIsEmpty, nodeIsElement } from "../../lib/dom";
import { ascend } from "../../lib/node";
export interface ChildNodeRange {
parent: Node;
startIndex: number;
/* exclusive end */
endIndex: number;
}
/**
* Indices should be >= 0 and startIndex < endIndex
*/
function makeChildNodeRange(
node: Node,
startIndex: number,
endIndex = startIndex + 1,
): ChildNodeRange {
return {
parent: node,
startIndex,
endIndex,
};
}
/**
* Result does not indicate the node itself but a supposed new node that
* entirely surrounds the passed in node
*/
export function nodeToChildNodeRange(node: Node): ChildNodeRange {
const parent = ascend(node);
const index = Array.prototype.indexOf.call(parent.childNodes, node);
return makeChildNodeRange(parent, index);
}
function toDOMRange(childNodeRange: ChildNodeRange): Range {
const range = new Range();
range.setStart(childNodeRange.parent, childNodeRange.startIndex);
range.setEnd(childNodeRange.parent, childNodeRange.endIndex);
return range;
}
export function areSiblingChildNodeRanges(
before: ChildNodeRange,
after: ChildNodeRange,
): boolean {
if (before.parent !== after.parent || before.endIndex > after.startIndex) {
return false;
}
if (before.endIndex === after.startIndex) {
return true;
}
for (let index = before.endIndex; index < after.startIndex; index++) {
const node = before.parent.childNodes[index];
if (!nodeIsElement(node) || !elementIsEmpty(node)) {
return false;
}
}
return true;
}
export function coversWholeParent(childNodeRange: ChildNodeRange): boolean {
return (
childNodeRange.startIndex === 0 &&
childNodeRange.endIndex === childNodeRange.parent.childNodes.length
);
}
/**
* Precondition: must be sibling child node ranges
*/
export function mergeChildNodeRanges(
before: ChildNodeRange,
after: ChildNodeRange,
): ChildNodeRange {
return {
parent: before.parent,
startIndex: before.startIndex,
endIndex: after.endIndex,
};
}
export function surroundChildNodeRangeWithNode(
childNodeRange: ChildNodeRange,
node: Node,
): void {
const range = toDOMRange(childNodeRange);
if (range.collapsed) {
/**
* If the range is collapsed to a single element, move the range inside the element.
* This prevents putting the surround above the base element.
*/
const selected = range.commonAncestorContainer.childNodes[range.startOffset];
if (nodeIsElement(selected)) {
range.selectNode(selected);
}
}
range.surroundContents(node);
}