Commit 008b21d9 authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Migrate range-utils module to TypeScript

parent 0ad6ce49
/** /**
* Returns true if the start point of a selection occurs after the end point, * Returns true if the start point of a selection occurs after the end point,
* in document order. * in document order.
*
* @param {Selection} selection
*/ */
export function isSelectionBackwards(selection) { export function isSelectionBackwards(selection: Selection) {
if (selection.focusNode === selection.anchorNode) { if (selection.focusNode === selection.anchorNode) {
return selection.focusOffset < selection.anchorOffset; return selection.focusOffset < selection.anchorOffset;
} }
...@@ -17,11 +15,8 @@ export function isSelectionBackwards(selection) { ...@@ -17,11 +15,8 @@ export function isSelectionBackwards(selection) {
/** /**
* Returns true if any part of `node` lies within `range`. * Returns true if any part of `node` lies within `range`.
*
* @param {Range} range
* @param {Node} node
*/ */
export function isNodeInRange(range, node) { export function isNodeInRange(range: Range, node: Node) {
try { try {
const length = node.nodeValue?.length ?? node.childNodes.length; const length = node.nodeValue?.length ?? node.childNodes.length;
return ( return (
...@@ -40,15 +35,13 @@ export function isNodeInRange(range, node) { ...@@ -40,15 +35,13 @@ export function isNodeInRange(range, node) {
/** /**
* Iterate over all Node(s) which overlap `range` in document order and invoke * Iterate over all Node(s) which overlap `range` in document order and invoke
* `callback` for each of them. * `callback` for each of them.
*
* @param {Range} range
* @param {(n: Node) => void} callback
*/ */
export function forEachNodeInRange(range, callback) { export function forEachNodeInRange(range: Range, callback: (n: Node) => void) {
const root = range.commonAncestorContainer; const root = range.commonAncestorContainer;
const nodeIter = /** @type {Document} */ ( const nodeIter: NodeIterator = root.ownerDocument!.createNodeIterator(
root.ownerDocument root,
).createNodeIterator(root, NodeFilter.SHOW_ALL); NodeFilter.SHOW_ALL
);
let currentNode; let currentNode;
while ((currentNode = nodeIter.nextNode())) { while ((currentNode = nodeIter.nextNode())) {
...@@ -58,26 +51,27 @@ export function forEachNodeInRange(range, callback) { ...@@ -58,26 +51,27 @@ export function forEachNodeInRange(range, callback) {
} }
} }
function nodeIsText(node: Node): node is Text {
const whitespaceOnly = /^\s*$/;
return (
node.nodeType === Node.TEXT_NODE && !node.textContent!.match(whitespaceOnly)
);
}
/** /**
* Returns the bounding rectangles of non-whitespace text nodes in `range`. * Returns the bounding rectangles of non-whitespace text nodes in `range`.
* *
* @param {Range} range * @return Array of bounding rects in viewport coordinates.
* @return {Array<DOMRect>} Array of bounding rects in viewport coordinates.
*/ */
export function getTextBoundingBoxes(range) { export function getTextBoundingBoxes(range: Range): DOMRect[] {
const whitespaceOnly = /^\s*$/; const textNodes: Text[] = [];
const textNodes = /** @type {Text[]} */ ([]);
forEachNodeInRange(range, node => { forEachNodeInRange(range, node => {
if ( if (nodeIsText(node)) {
node.nodeType === Node.TEXT_NODE && textNodes.push(node);
!(/** @type {string} */ (node.textContent).match(whitespaceOnly))
) {
textNodes.push(/** @type {Text} */ (node));
} }
}); });
/** @type {DOMRect[]} */ let rects: DOMRect[] = [];
let rects = [];
textNodes.forEach(node => { textNodes.forEach(node => {
const nodeRange = node.ownerDocument.createRange(); const nodeRange = node.ownerDocument.createRange();
nodeRange.selectNodeContents(node); nodeRange.selectNodeContents(node);
...@@ -106,11 +100,8 @@ export function getTextBoundingBoxes(range) { ...@@ -106,11 +100,8 @@ export function getTextBoundingBoxes(range) {
* containing the focus point of a Selection. * containing the focus point of a Selection.
* *
* Returns null if the selection is empty. * Returns null if the selection is empty.
*
* @param {Selection} selection
* @return {DOMRect|null}
*/ */
export function selectionFocusRect(selection) { export function selectionFocusRect(selection: Selection): DOMRect | null {
if (selection.isCollapsed) { if (selection.isCollapsed) {
return null; return null;
} }
...@@ -132,29 +123,23 @@ export function selectionFocusRect(selection) { ...@@ -132,29 +123,23 @@ export function selectionFocusRect(selection) {
* An `item` can be any data that the caller wishes to compute from or associate * An `item` can be any data that the caller wishes to compute from or associate
* with a node. Only unique items, as determined by `Object.is`, are returned. * with a node. Only unique items, as determined by `Object.is`, are returned.
* *
* @template T * @param itemForNode - Callback returning the item for a given node
* @param {Range} range
* @param {(n: Node) => T} itemForNode - Callback returning the item for a given node
* @return {NonNullable<T>[]} items
*/ */
export function itemsForRange(range, itemForNode) { export function itemsForRange<T>(
/** @type {Set<Node>} */ range: Range,
const checkedNodes = new Set(); itemForNode: (n: Node) => NonNullable<T> | null | undefined
/** @type {Set<NonNullable<T>>} */ ): NonNullable<T>[] {
const items = new Set(); const checkedNodes = new Set<Node>();
const items = new Set<NonNullable<T>>();
forEachNodeInRange(range, node => { forEachNodeInRange(range, (current: Node | null) => {
/** @type {Node|null} */
let current = node;
while (current) { while (current) {
if (checkedNodes.has(current)) { if (checkedNodes.has(current)) {
break; break;
} }
checkedNodes.add(current); checkedNodes.add(current);
const item = /** @type {NonNullable<T>|null|undefined} */ ( const item = itemForNode(current);
itemForNode(current)
);
if (item !== null && item !== undefined) { if (item !== null && item !== undefined) {
items.add(item); items.add(item);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment