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;
}
|