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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 1x 1x 28x 10x 10x 28x 28x 10x 10x 10x 10x 10x 18x 8x 8x 8x 14x 1x 13x 14x 8x | import { getRegularSelectionOffsets, getSelectionRootNode, handleRegularSelection, isNodeOfType, processChildNode, } from 'roosterjs-content-model-dom'; import type { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types'; /** * @internal */ interface FormatStateContext extends DomToModelContext { /** * An optional stack of parent elements to process. When provided, the child nodes of current parent element will be ignored, * but use the top element in this stack instead in childProcessor. */ nodeStack?: Node[]; } /** * @internal * In order to get format, we can still use the regular child processor. However, to improve performance, we don't need to create * content model for the whole doc, instead we only need to traverse the tree path that can arrive current selected node. * This "reduced" child processor will first create a node stack that stores DOM node from root to current common ancestor node of selection, * then use this stack as a faked DOM tree to create a reduced content model which we can use to retrieve format state */ export function reducedModelChildProcessor( group: ContentModelBlockGroup, parent: ParentNode, context: FormatStateContext ) { if (!context.nodeStack) { const selectionRootNode = getSelectionRootNode(context.selection); context.nodeStack = selectionRootNode ? createNodeStack(parent, selectionRootNode) : []; } const stackChild = context.nodeStack.pop(); if (stackChild) { const [nodeStartOffset, nodeEndOffset] = getRegularSelectionOffsets(context, parent); // If selection is not on this node, skip getting node index to save some time since we don't need it here const index = nodeStartOffset >= 0 || nodeEndOffset >= 0 ? getChildIndex(parent, stackChild) : -1; Iif (index >= 0) { handleRegularSelection(index, context, group, nodeStartOffset, nodeEndOffset); } processChildNode(group, stackChild, context); Iif (index >= 0) { handleRegularSelection(index + 1, context, group, nodeStartOffset, nodeEndOffset); } } else { // No child node from node stack, that means we have reached the deepest node of selection. // Now we can use default child processor to perform full sub tree scanning for content model, // So that all selected node will be included. context.defaultElementProcessors.child(group, parent, context); } } function createNodeStack(root: Node, startNode: Node): Node[] { const result: Node[] = []; let node: Node | null = startNode; while (node && root != node && root.contains(node)) { if (isNodeOfType(node, 'ELEMENT_NODE') && node.tagName == 'TABLE') { // For table, we can't do a reduced model creation since we need to handle their cells and indexes, // so clean up whatever we already have, and just put table into the stack result.splice(0, result.length, node); } else { result.push(node); } node = node.parentNode; } return result; } function getChildIndex(parent: ParentNode, stackChild: Node) { let index = 0; let child = parent.firstChild; while (child && child != stackChild) { index++; child = child.nextSibling; } return index; } |