All files / roosterjs-content-model-core/lib/coreApi/addUndoSnapshot getPath.ts

96.43% Statements 27/28
85% Branches 34/40
100% Functions 1/1
96.3% Lines 26/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 641x                               1x 306x     306x       306x 102x   102x 5x 5x     102x   204x 204x     306x 968x 968x   968x 248x 32x 2x     30x   216x     246x     968x 968x 968x     306x    
import { isNodeOfType } from 'roosterjs-content-model-dom';
 
/**
 * @internal
 *
 * Get the path of the node relative to rootNode.
 * The path of the node is an array of integer indices into the childNodes of the given node.
 *
 * The node path will be what the node path will be on a _normalized_ dom
 * (e.g. empty text nodes will be ignored and adjacent text nodes will be concatenated)
 *
 * @param rootNode the node the path will be relative to
 * @param position the position to get indexes from. Follows the same semantics
 * as selectionRange (if node is of type Text, it is an offset into the text of that node.
 * If node is of type Element, it is the index of a child in that Element node.)
 */
export function getPath(node: Node | null, offset: number, rootNode: Node): number[] {
    const result: number[] = [];
    let parent: Node | null;
 
    Iif (!node || !rootNode.contains(node)) {
        return result;
    }
 
    if (isNodeOfType(node, 'TEXT_NODE')) {
        parent = node.parentNode;
 
        while (node.previousSibling && isNodeOfType(node.previousSibling, 'TEXT_NODE')) {
            offset += node.previousSibling.nodeValue?.length || 0;
            node = node.previousSibling;
        }
 
        result.unshift(offset);
    } else {
        parent = node;
        node = node.childNodes[offset];
    }
 
    do {
        offset = 0;
        let isPreviousText = false;
 
        for (let c: Node | null = parent?.firstChild || null; c && c != node; c = c.nextSibling) {
            if (isNodeOfType(c, 'TEXT_NODE')) {
                if (c.nodeValue?.length === 0 || isPreviousText) {
                    continue;
                }
 
                isPreviousText = true;
            } else {
                isPreviousText = false;
            }
 
            offset++;
        }
 
        result.unshift(offset);
        node = parent;
        parent = parent?.parentNode || null;
    } while (node && node != rootNode);
 
    return result;
}