667 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
		
		
			
		
	
	
			667 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Copyright (c) Meta Platforms, Inc. and affiliates.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This source code is licensed under the MIT license found in the
							 | 
						||
| 
								 | 
							
								 * LICENSE file in the root directory of this source tree.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import type {Binding} from '.';
							 | 
						||
| 
								 | 
							
								import type {ElementNode, NodeKey, NodeMap} from 'lexical';
							 | 
						||
| 
								 | 
							
								import type {AbstractType, Map as YMap, XmlElement, XmlText} from 'yjs';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import {$createChildrenArray} from '@lexical/offset';
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
								  $getNodeByKey,
							 | 
						||
| 
								 | 
							
								  $isDecoratorNode,
							 | 
						||
| 
								 | 
							
								  $isElementNode,
							 | 
						||
| 
								 | 
							
								  $isTextNode,
							 | 
						||
| 
								 | 
							
								} from 'lexical';
							 | 
						||
| 
								 | 
							
								import invariant from 'lexical/shared/invariant';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import {CollabDecoratorNode} from './CollabDecoratorNode';
							 | 
						||
| 
								 | 
							
								import {CollabLineBreakNode} from './CollabLineBreakNode';
							 | 
						||
| 
								 | 
							
								import {CollabTextNode} from './CollabTextNode';
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
								  $createCollabNodeFromLexicalNode,
							 | 
						||
| 
								 | 
							
								  $getNodeByKeyOrThrow,
							 | 
						||
| 
								 | 
							
								  $getOrInitCollabNodeFromSharedType,
							 | 
						||
| 
								 | 
							
								  createLexicalNodeFromCollabNode,
							 | 
						||
| 
								 | 
							
								  getPositionFromElementAndOffset,
							 | 
						||
| 
								 | 
							
								  removeFromParent,
							 | 
						||
| 
								 | 
							
								  spliceString,
							 | 
						||
| 
								 | 
							
								  syncPropertiesFromLexical,
							 | 
						||
| 
								 | 
							
								  syncPropertiesFromYjs,
							 | 
						||
| 
								 | 
							
								} from './Utils';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type IntentionallyMarkedAsDirtyElement = boolean;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export class CollabElementNode {
							 | 
						||
| 
								 | 
							
								  _key: NodeKey;
							 | 
						||
| 
								 | 
							
								  _children: Array<
							 | 
						||
| 
								 | 
							
								    | CollabElementNode
							 | 
						||
| 
								 | 
							
								    | CollabTextNode
							 | 
						||
| 
								 | 
							
								    | CollabDecoratorNode
							 | 
						||
| 
								 | 
							
								    | CollabLineBreakNode
							 | 
						||
| 
								 | 
							
								  >;
							 | 
						||
| 
								 | 
							
								  _xmlText: XmlText;
							 | 
						||
| 
								 | 
							
								  _type: string;
							 | 
						||
| 
								 | 
							
								  _parent: null | CollabElementNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  constructor(
							 | 
						||
| 
								 | 
							
								    xmlText: XmlText,
							 | 
						||
| 
								 | 
							
								    parent: null | CollabElementNode,
							 | 
						||
| 
								 | 
							
								    type: string,
							 | 
						||
| 
								 | 
							
								  ) {
							 | 
						||
| 
								 | 
							
								    this._key = '';
							 | 
						||
| 
								 | 
							
								    this._children = [];
							 | 
						||
| 
								 | 
							
								    this._xmlText = xmlText;
							 | 
						||
| 
								 | 
							
								    this._type = type;
							 | 
						||
| 
								 | 
							
								    this._parent = parent;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getPrevNode(nodeMap: null | NodeMap): null | ElementNode {
							 | 
						||
| 
								 | 
							
								    if (nodeMap === null) {
							 | 
						||
| 
								 | 
							
								      return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const node = nodeMap.get(this._key);
							 | 
						||
| 
								 | 
							
								    return $isElementNode(node) ? node : null;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getNode(): null | ElementNode {
							 | 
						||
| 
								 | 
							
								    const node = $getNodeByKey(this._key);
							 | 
						||
| 
								 | 
							
								    return $isElementNode(node) ? node : null;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getSharedType(): XmlText {
							 | 
						||
| 
								 | 
							
								    return this._xmlText;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getType(): string {
							 | 
						||
| 
								 | 
							
								    return this._type;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getKey(): NodeKey {
							 | 
						||
| 
								 | 
							
								    return this._key;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  isEmpty(): boolean {
							 | 
						||
| 
								 | 
							
								    return this._children.length === 0;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getSize(): number {
							 | 
						||
| 
								 | 
							
								    return 1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getOffset(): number {
							 | 
						||
| 
								 | 
							
								    const collabElementNode = this._parent;
							 | 
						||
| 
								 | 
							
								    invariant(
							 | 
						||
| 
								 | 
							
								      collabElementNode !== null,
							 | 
						||
| 
								 | 
							
								      'getOffset: could not find collab element node',
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return collabElementNode.getChildOffset(this);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  syncPropertiesFromYjs(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    keysChanged: null | Set<string>,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const lexicalNode = this.getNode();
							 | 
						||
| 
								 | 
							
								    invariant(
							 | 
						||
| 
								 | 
							
								      lexicalNode !== null,
							 | 
						||
| 
								 | 
							
								      'syncPropertiesFromYjs: could not find element node',
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								    syncPropertiesFromYjs(binding, this._xmlText, lexicalNode, keysChanged);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  applyChildrenYjsDelta(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    deltas: Array<{
							 | 
						||
| 
								 | 
							
								      insert?: string | object | AbstractType<unknown>;
							 | 
						||
| 
								 | 
							
								      delete?: number;
							 | 
						||
| 
								 | 
							
								      retain?: number;
							 | 
						||
| 
								 | 
							
								      attributes?: {
							 | 
						||
| 
								 | 
							
								        [x: string]: unknown;
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    }>,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const children = this._children;
							 | 
						||
| 
								 | 
							
								    let currIndex = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < deltas.length; i++) {
							 | 
						||
| 
								 | 
							
								      const delta = deltas[i];
							 | 
						||
| 
								 | 
							
								      const insertDelta = delta.insert;
							 | 
						||
| 
								 | 
							
								      const deleteDelta = delta.delete;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (delta.retain != null) {
							 | 
						||
| 
								 | 
							
								        currIndex += delta.retain;
							 | 
						||
| 
								 | 
							
								      } else if (typeof deleteDelta === 'number') {
							 | 
						||
| 
								 | 
							
								        let deletionSize = deleteDelta;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while (deletionSize > 0) {
							 | 
						||
| 
								 | 
							
								          const {node, nodeIndex, offset, length} =
							 | 
						||
| 
								 | 
							
								            getPositionFromElementAndOffset(this, currIndex, false);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (
							 | 
						||
| 
								 | 
							
								            node instanceof CollabElementNode ||
							 | 
						||
| 
								 | 
							
								            node instanceof CollabLineBreakNode ||
							 | 
						||
| 
								 | 
							
								            node instanceof CollabDecoratorNode
							 | 
						||
| 
								 | 
							
								          ) {
							 | 
						||
| 
								 | 
							
								            children.splice(nodeIndex, 1);
							 | 
						||
| 
								 | 
							
								            deletionSize -= 1;
							 | 
						||
| 
								 | 
							
								          } else if (node instanceof CollabTextNode) {
							 | 
						||
| 
								 | 
							
								            const delCount = Math.min(deletionSize, length);
							 | 
						||
| 
								 | 
							
								            const prevCollabNode =
							 | 
						||
| 
								 | 
							
								              nodeIndex !== 0 ? children[nodeIndex - 1] : null;
							 | 
						||
| 
								 | 
							
								            const nodeSize = node.getSize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (
							 | 
						||
| 
								 | 
							
								              offset === 0 &&
							 | 
						||
| 
								 | 
							
								              delCount === 1 &&
							 | 
						||
| 
								 | 
							
								              nodeIndex > 0 &&
							 | 
						||
| 
								 | 
							
								              prevCollabNode instanceof CollabTextNode &&
							 | 
						||
| 
								 | 
							
								              length === nodeSize &&
							 | 
						||
| 
								 | 
							
								              // If the node has no keys, it's been deleted
							 | 
						||
| 
								 | 
							
								              Array.from(node._map.keys()).length === 0
							 | 
						||
| 
								 | 
							
								            ) {
							 | 
						||
| 
								 | 
							
								              // Merge the text node with previous.
							 | 
						||
| 
								 | 
							
								              prevCollabNode._text += node._text;
							 | 
						||
| 
								 | 
							
								              children.splice(nodeIndex, 1);
							 | 
						||
| 
								 | 
							
								            } else if (offset === 0 && delCount === nodeSize) {
							 | 
						||
| 
								 | 
							
								              // The entire thing needs removing
							 | 
						||
| 
								 | 
							
								              children.splice(nodeIndex, 1);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								              node._text = spliceString(node._text, offset, delCount, '');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            deletionSize -= delCount;
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            // Can occur due to the deletion from the dangling text heuristic below.
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else if (insertDelta != null) {
							 | 
						||
| 
								 | 
							
								        if (typeof insertDelta === 'string') {
							 | 
						||
| 
								 | 
							
								          const {node, offset} = getPositionFromElementAndOffset(
							 | 
						||
| 
								 | 
							
								            this,
							 | 
						||
| 
								 | 
							
								            currIndex,
							 | 
						||
| 
								 | 
							
								            true,
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (node instanceof CollabTextNode) {
							 | 
						||
| 
								 | 
							
								            node._text = spliceString(node._text, offset, 0, insertDelta);
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            // TODO: maybe we can improve this by keeping around a redundant
							 | 
						||
| 
								 | 
							
								            // text node map, rather than removing all the text nodes, so there
							 | 
						||
| 
								 | 
							
								            // never can be dangling text.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // We have a conflict where there was likely a CollabTextNode and
							 | 
						||
| 
								 | 
							
								            // an Lexical TextNode too, but they were removed in a merge. So
							 | 
						||
| 
								 | 
							
								            // let's just ignore the text and trigger a removal for it from our
							 | 
						||
| 
								 | 
							
								            // shared type.
							 | 
						||
| 
								 | 
							
								            this._xmlText.delete(offset, insertDelta.length);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          currIndex += insertDelta.length;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          const sharedType = insertDelta;
							 | 
						||
| 
								 | 
							
								          const {nodeIndex} = getPositionFromElementAndOffset(
							 | 
						||
| 
								 | 
							
								            this,
							 | 
						||
| 
								 | 
							
								            currIndex,
							 | 
						||
| 
								 | 
							
								            false,
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								          const collabNode = $getOrInitCollabNodeFromSharedType(
							 | 
						||
| 
								 | 
							
								            binding,
							 | 
						||
| 
								 | 
							
								            sharedType as XmlText | YMap<unknown> | XmlElement,
							 | 
						||
| 
								 | 
							
								            this,
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								          children.splice(nodeIndex, 0, collabNode);
							 | 
						||
| 
								 | 
							
								          currIndex += 1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        throw new Error('Unexpected delta format');
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  syncChildrenFromYjs(binding: Binding): void {
							 | 
						||
| 
								 | 
							
								    // Now diff the children of the collab node with that of our existing Lexical node.
							 | 
						||
| 
								 | 
							
								    const lexicalNode = this.getNode();
							 | 
						||
| 
								 | 
							
								    invariant(
							 | 
						||
| 
								 | 
							
								      lexicalNode !== null,
							 | 
						||
| 
								 | 
							
								      'syncChildrenFromYjs: could not find element node',
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const key = lexicalNode.__key;
							 | 
						||
| 
								 | 
							
								    const prevLexicalChildrenKeys = $createChildrenArray(lexicalNode, null);
							 | 
						||
| 
								 | 
							
								    const nextLexicalChildrenKeys: Array<NodeKey> = [];
							 | 
						||
| 
								 | 
							
								    const lexicalChildrenKeysLength = prevLexicalChildrenKeys.length;
							 | 
						||
| 
								 | 
							
								    const collabChildren = this._children;
							 | 
						||
| 
								 | 
							
								    const collabChildrenLength = collabChildren.length;
							 | 
						||
| 
								 | 
							
								    const collabNodeMap = binding.collabNodeMap;
							 | 
						||
| 
								 | 
							
								    const visitedKeys = new Set();
							 | 
						||
| 
								 | 
							
								    let collabKeys;
							 | 
						||
| 
								 | 
							
								    let writableLexicalNode;
							 | 
						||
| 
								 | 
							
								    let prevIndex = 0;
							 | 
						||
| 
								 | 
							
								    let prevChildNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (collabChildrenLength !== lexicalChildrenKeysLength) {
							 | 
						||
| 
								 | 
							
								      writableLexicalNode = lexicalNode.getWritable();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < collabChildrenLength; i++) {
							 | 
						||
| 
								 | 
							
								      const lexicalChildKey = prevLexicalChildrenKeys[prevIndex];
							 | 
						||
| 
								 | 
							
								      const childCollabNode = collabChildren[i];
							 | 
						||
| 
								 | 
							
								      const collabLexicalChildNode = childCollabNode.getNode();
							 | 
						||
| 
								 | 
							
								      const collabKey = childCollabNode._key;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (collabLexicalChildNode !== null && lexicalChildKey === collabKey) {
							 | 
						||
| 
								 | 
							
								        const childNeedsUpdating = $isTextNode(collabLexicalChildNode);
							 | 
						||
| 
								 | 
							
								        // Update
							 | 
						||
| 
								 | 
							
								        visitedKeys.add(lexicalChildKey);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (childNeedsUpdating) {
							 | 
						||
| 
								 | 
							
								          childCollabNode._key = lexicalChildKey;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (childCollabNode instanceof CollabElementNode) {
							 | 
						||
| 
								 | 
							
								            const xmlText = childCollabNode._xmlText;
							 | 
						||
| 
								 | 
							
								            childCollabNode.syncPropertiesFromYjs(binding, null);
							 | 
						||
| 
								 | 
							
								            childCollabNode.applyChildrenYjsDelta(binding, xmlText.toDelta());
							 | 
						||
| 
								 | 
							
								            childCollabNode.syncChildrenFromYjs(binding);
							 | 
						||
| 
								 | 
							
								          } else if (childCollabNode instanceof CollabTextNode) {
							 | 
						||
| 
								 | 
							
								            childCollabNode.syncPropertiesAndTextFromYjs(binding, null);
							 | 
						||
| 
								 | 
							
								          } else if (childCollabNode instanceof CollabDecoratorNode) {
							 | 
						||
| 
								 | 
							
								            childCollabNode.syncPropertiesFromYjs(binding, null);
							 | 
						||
| 
								 | 
							
								          } else if (!(childCollabNode instanceof CollabLineBreakNode)) {
							 | 
						||
| 
								 | 
							
								            invariant(
							 | 
						||
| 
								 | 
							
								              false,
							 | 
						||
| 
								 | 
							
								              'syncChildrenFromYjs: expected text, element, decorator, or linebreak collab node',
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        nextLexicalChildrenKeys[i] = lexicalChildKey;
							 | 
						||
| 
								 | 
							
								        prevChildNode = collabLexicalChildNode;
							 | 
						||
| 
								 | 
							
								        prevIndex++;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        if (collabKeys === undefined) {
							 | 
						||
| 
								 | 
							
								          collabKeys = new Set();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          for (let s = 0; s < collabChildrenLength; s++) {
							 | 
						||
| 
								 | 
							
								            const child = collabChildren[s];
							 | 
						||
| 
								 | 
							
								            const childKey = child._key;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (childKey !== '') {
							 | 
						||
| 
								 | 
							
								              collabKeys.add(childKey);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (
							 | 
						||
| 
								 | 
							
								          collabLexicalChildNode !== null &&
							 | 
						||
| 
								 | 
							
								          lexicalChildKey !== undefined &&
							 | 
						||
| 
								 | 
							
								          !collabKeys.has(lexicalChildKey)
							 | 
						||
| 
								 | 
							
								        ) {
							 | 
						||
| 
								 | 
							
								          const nodeToRemove = $getNodeByKeyOrThrow(lexicalChildKey);
							 | 
						||
| 
								 | 
							
								          removeFromParent(nodeToRemove);
							 | 
						||
| 
								 | 
							
								          i--;
							 | 
						||
| 
								 | 
							
								          prevIndex++;
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        writableLexicalNode = lexicalNode.getWritable();
							 | 
						||
| 
								 | 
							
								        // Create/Replace
							 | 
						||
| 
								 | 
							
								        const lexicalChildNode = createLexicalNodeFromCollabNode(
							 | 
						||
| 
								 | 
							
								          binding,
							 | 
						||
| 
								 | 
							
								          childCollabNode,
							 | 
						||
| 
								 | 
							
								          key,
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        const childKey = lexicalChildNode.__key;
							 | 
						||
| 
								 | 
							
								        collabNodeMap.set(childKey, childCollabNode);
							 | 
						||
| 
								 | 
							
								        nextLexicalChildrenKeys[i] = childKey;
							 | 
						||
| 
								 | 
							
								        if (prevChildNode === null) {
							 | 
						||
| 
								 | 
							
								          const nextSibling = writableLexicalNode.getFirstChild();
							 | 
						||
| 
								 | 
							
								          writableLexicalNode.__first = childKey;
							 | 
						||
| 
								 | 
							
								          if (nextSibling !== null) {
							 | 
						||
| 
								 | 
							
								            const writableNextSibling = nextSibling.getWritable();
							 | 
						||
| 
								 | 
							
								            writableNextSibling.__prev = childKey;
							 | 
						||
| 
								 | 
							
								            lexicalChildNode.__next = writableNextSibling.__key;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          const writablePrevChildNode = prevChildNode.getWritable();
							 | 
						||
| 
								 | 
							
								          const nextSibling = prevChildNode.getNextSibling();
							 | 
						||
| 
								 | 
							
								          writablePrevChildNode.__next = childKey;
							 | 
						||
| 
								 | 
							
								          lexicalChildNode.__prev = prevChildNode.__key;
							 | 
						||
| 
								 | 
							
								          if (nextSibling !== null) {
							 | 
						||
| 
								 | 
							
								            const writableNextSibling = nextSibling.getWritable();
							 | 
						||
| 
								 | 
							
								            writableNextSibling.__prev = childKey;
							 | 
						||
| 
								 | 
							
								            lexicalChildNode.__next = writableNextSibling.__key;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (i === collabChildrenLength - 1) {
							 | 
						||
| 
								 | 
							
								          writableLexicalNode.__last = childKey;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        writableLexicalNode.__size++;
							 | 
						||
| 
								 | 
							
								        prevChildNode = lexicalChildNode;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < lexicalChildrenKeysLength; i++) {
							 | 
						||
| 
								 | 
							
								      const lexicalChildKey = prevLexicalChildrenKeys[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!visitedKeys.has(lexicalChildKey)) {
							 | 
						||
| 
								 | 
							
								        // Remove
							 | 
						||
| 
								 | 
							
								        const lexicalChildNode = $getNodeByKeyOrThrow(lexicalChildKey);
							 | 
						||
| 
								 | 
							
								        const collabNode = binding.collabNodeMap.get(lexicalChildKey);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (collabNode !== undefined) {
							 | 
						||
| 
								 | 
							
								          collabNode.destroy(binding);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        removeFromParent(lexicalChildNode);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  syncPropertiesFromLexical(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    nextLexicalNode: ElementNode,
							 | 
						||
| 
								 | 
							
								    prevNodeMap: null | NodeMap,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    syncPropertiesFromLexical(
							 | 
						||
| 
								 | 
							
								      binding,
							 | 
						||
| 
								 | 
							
								      this._xmlText,
							 | 
						||
| 
								 | 
							
								      this.getPrevNode(prevNodeMap),
							 | 
						||
| 
								 | 
							
								      nextLexicalNode,
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _syncChildFromLexical(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    index: number,
							 | 
						||
| 
								 | 
							
								    key: NodeKey,
							 | 
						||
| 
								 | 
							
								    prevNodeMap: null | NodeMap,
							 | 
						||
| 
								 | 
							
								    dirtyElements: null | Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
							 | 
						||
| 
								 | 
							
								    dirtyLeaves: null | Set<NodeKey>,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const childCollabNode = this._children[index];
							 | 
						||
| 
								 | 
							
								    // Update
							 | 
						||
| 
								 | 
							
								    const nextChildNode = $getNodeByKeyOrThrow(key);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (
							 | 
						||
| 
								 | 
							
								      childCollabNode instanceof CollabElementNode &&
							 | 
						||
| 
								 | 
							
								      $isElementNode(nextChildNode)
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								      childCollabNode.syncPropertiesFromLexical(
							 | 
						||
| 
								 | 
							
								        binding,
							 | 
						||
| 
								 | 
							
								        nextChildNode,
							 | 
						||
| 
								 | 
							
								        prevNodeMap,
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								      childCollabNode.syncChildrenFromLexical(
							 | 
						||
| 
								 | 
							
								        binding,
							 | 
						||
| 
								 | 
							
								        nextChildNode,
							 | 
						||
| 
								 | 
							
								        prevNodeMap,
							 | 
						||
| 
								 | 
							
								        dirtyElements,
							 | 
						||
| 
								 | 
							
								        dirtyLeaves,
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								    } else if (
							 | 
						||
| 
								 | 
							
								      childCollabNode instanceof CollabTextNode &&
							 | 
						||
| 
								 | 
							
								      $isTextNode(nextChildNode)
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								      childCollabNode.syncPropertiesAndTextFromLexical(
							 | 
						||
| 
								 | 
							
								        binding,
							 | 
						||
| 
								 | 
							
								        nextChildNode,
							 | 
						||
| 
								 | 
							
								        prevNodeMap,
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								    } else if (
							 | 
						||
| 
								 | 
							
								      childCollabNode instanceof CollabDecoratorNode &&
							 | 
						||
| 
								 | 
							
								      $isDecoratorNode(nextChildNode)
							 | 
						||
| 
								 | 
							
								    ) {
							 | 
						||
| 
								 | 
							
								      childCollabNode.syncPropertiesFromLexical(
							 | 
						||
| 
								 | 
							
								        binding,
							 | 
						||
| 
								 | 
							
								        nextChildNode,
							 | 
						||
| 
								 | 
							
								        prevNodeMap,
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  syncChildrenFromLexical(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    nextLexicalNode: ElementNode,
							 | 
						||
| 
								 | 
							
								    prevNodeMap: null | NodeMap,
							 | 
						||
| 
								 | 
							
								    dirtyElements: null | Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
							 | 
						||
| 
								 | 
							
								    dirtyLeaves: null | Set<NodeKey>,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const prevLexicalNode = this.getPrevNode(prevNodeMap);
							 | 
						||
| 
								 | 
							
								    const prevChildren =
							 | 
						||
| 
								 | 
							
								      prevLexicalNode === null
							 | 
						||
| 
								 | 
							
								        ? []
							 | 
						||
| 
								 | 
							
								        : $createChildrenArray(prevLexicalNode, prevNodeMap);
							 | 
						||
| 
								 | 
							
								    const nextChildren = $createChildrenArray(nextLexicalNode, null);
							 | 
						||
| 
								 | 
							
								    const prevEndIndex = prevChildren.length - 1;
							 | 
						||
| 
								 | 
							
								    const nextEndIndex = nextChildren.length - 1;
							 | 
						||
| 
								 | 
							
								    const collabNodeMap = binding.collabNodeMap;
							 | 
						||
| 
								 | 
							
								    let prevChildrenSet: Set<NodeKey> | undefined;
							 | 
						||
| 
								 | 
							
								    let nextChildrenSet: Set<NodeKey> | undefined;
							 | 
						||
| 
								 | 
							
								    let prevIndex = 0;
							 | 
						||
| 
								 | 
							
								    let nextIndex = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (prevIndex <= prevEndIndex && nextIndex <= nextEndIndex) {
							 | 
						||
| 
								 | 
							
								      const prevKey = prevChildren[prevIndex];
							 | 
						||
| 
								 | 
							
								      const nextKey = nextChildren[nextIndex];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (prevKey === nextKey) {
							 | 
						||
| 
								 | 
							
								        // Nove move, create or remove
							 | 
						||
| 
								 | 
							
								        this._syncChildFromLexical(
							 | 
						||
| 
								 | 
							
								          binding,
							 | 
						||
| 
								 | 
							
								          nextIndex,
							 | 
						||
| 
								 | 
							
								          nextKey,
							 | 
						||
| 
								 | 
							
								          prevNodeMap,
							 | 
						||
| 
								 | 
							
								          dirtyElements,
							 | 
						||
| 
								 | 
							
								          dirtyLeaves,
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        prevIndex++;
							 | 
						||
| 
								 | 
							
								        nextIndex++;
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        if (prevChildrenSet === undefined) {
							 | 
						||
| 
								 | 
							
								          prevChildrenSet = new Set(prevChildren);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (nextChildrenSet === undefined) {
							 | 
						||
| 
								 | 
							
								          nextChildrenSet = new Set(nextChildren);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const nextHasPrevKey = nextChildrenSet.has(prevKey);
							 | 
						||
| 
								 | 
							
								        const prevHasNextKey = prevChildrenSet.has(nextKey);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!nextHasPrevKey) {
							 | 
						||
| 
								 | 
							
								          // Remove
							 | 
						||
| 
								 | 
							
								          this.splice(binding, nextIndex, 1);
							 | 
						||
| 
								 | 
							
								          prevIndex++;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          // Create or replace
							 | 
						||
| 
								 | 
							
								          const nextChildNode = $getNodeByKeyOrThrow(nextKey);
							 | 
						||
| 
								 | 
							
								          const collabNode = $createCollabNodeFromLexicalNode(
							 | 
						||
| 
								 | 
							
								            binding,
							 | 
						||
| 
								 | 
							
								            nextChildNode,
							 | 
						||
| 
								 | 
							
								            this,
							 | 
						||
| 
								 | 
							
								          );
							 | 
						||
| 
								 | 
							
								          collabNodeMap.set(nextKey, collabNode);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (prevHasNextKey) {
							 | 
						||
| 
								 | 
							
								            this.splice(binding, nextIndex, 1, collabNode);
							 | 
						||
| 
								 | 
							
								            prevIndex++;
							 | 
						||
| 
								 | 
							
								            nextIndex++;
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            this.splice(binding, nextIndex, 0, collabNode);
							 | 
						||
| 
								 | 
							
								            nextIndex++;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const appendNewChildren = prevIndex > prevEndIndex;
							 | 
						||
| 
								 | 
							
								    const removeOldChildren = nextIndex > nextEndIndex;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (appendNewChildren && !removeOldChildren) {
							 | 
						||
| 
								 | 
							
								      for (; nextIndex <= nextEndIndex; ++nextIndex) {
							 | 
						||
| 
								 | 
							
								        const key = nextChildren[nextIndex];
							 | 
						||
| 
								 | 
							
								        const nextChildNode = $getNodeByKeyOrThrow(key);
							 | 
						||
| 
								 | 
							
								        const collabNode = $createCollabNodeFromLexicalNode(
							 | 
						||
| 
								 | 
							
								          binding,
							 | 
						||
| 
								 | 
							
								          nextChildNode,
							 | 
						||
| 
								 | 
							
								          this,
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        this.append(collabNode);
							 | 
						||
| 
								 | 
							
								        collabNodeMap.set(key, collabNode);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else if (removeOldChildren && !appendNewChildren) {
							 | 
						||
| 
								 | 
							
								      for (let i = this._children.length - 1; i >= nextIndex; i--) {
							 | 
						||
| 
								 | 
							
								        this.splice(binding, i, 1);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  append(
							 | 
						||
| 
								 | 
							
								    collabNode:
							 | 
						||
| 
								 | 
							
								      | CollabElementNode
							 | 
						||
| 
								 | 
							
								      | CollabDecoratorNode
							 | 
						||
| 
								 | 
							
								      | CollabTextNode
							 | 
						||
| 
								 | 
							
								      | CollabLineBreakNode,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const xmlText = this._xmlText;
							 | 
						||
| 
								 | 
							
								    const children = this._children;
							 | 
						||
| 
								 | 
							
								    const lastChild = children[children.length - 1];
							 | 
						||
| 
								 | 
							
								    const offset =
							 | 
						||
| 
								 | 
							
								      lastChild !== undefined ? lastChild.getOffset() + lastChild.getSize() : 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (collabNode instanceof CollabElementNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._xmlText);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabTextNode) {
							 | 
						||
| 
								 | 
							
								      const map = collabNode._map;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (map.parent === null) {
							 | 
						||
| 
								 | 
							
								        xmlText.insertEmbed(offset, map);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      xmlText.insert(offset + 1, collabNode._text);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabLineBreakNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._map);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabDecoratorNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._xmlElem);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._children.push(collabNode);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  splice(
							 | 
						||
| 
								 | 
							
								    binding: Binding,
							 | 
						||
| 
								 | 
							
								    index: number,
							 | 
						||
| 
								 | 
							
								    delCount: number,
							 | 
						||
| 
								 | 
							
								    collabNode?:
							 | 
						||
| 
								 | 
							
								      | CollabElementNode
							 | 
						||
| 
								 | 
							
								      | CollabDecoratorNode
							 | 
						||
| 
								 | 
							
								      | CollabTextNode
							 | 
						||
| 
								 | 
							
								      | CollabLineBreakNode,
							 | 
						||
| 
								 | 
							
								  ): void {
							 | 
						||
| 
								 | 
							
								    const children = this._children;
							 | 
						||
| 
								 | 
							
								    const child = children[index];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (child === undefined) {
							 | 
						||
| 
								 | 
							
								      invariant(
							 | 
						||
| 
								 | 
							
								        collabNode !== undefined,
							 | 
						||
| 
								 | 
							
								        'splice: could not find collab element node',
							 | 
						||
| 
								 | 
							
								      );
							 | 
						||
| 
								 | 
							
								      this.append(collabNode);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const offset = child.getOffset();
							 | 
						||
| 
								 | 
							
								    invariant(offset !== -1, 'splice: expected offset to be greater than zero');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const xmlText = this._xmlText;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (delCount !== 0) {
							 | 
						||
| 
								 | 
							
								      // What if we delete many nodes, don't we need to get all their
							 | 
						||
| 
								 | 
							
								      // sizes?
							 | 
						||
| 
								 | 
							
								      xmlText.delete(offset, child.getSize());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (collabNode instanceof CollabElementNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._xmlText);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabTextNode) {
							 | 
						||
| 
								 | 
							
								      const map = collabNode._map;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (map.parent === null) {
							 | 
						||
| 
								 | 
							
								        xmlText.insertEmbed(offset, map);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      xmlText.insert(offset + 1, collabNode._text);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabLineBreakNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._map);
							 | 
						||
| 
								 | 
							
								    } else if (collabNode instanceof CollabDecoratorNode) {
							 | 
						||
| 
								 | 
							
								      xmlText.insertEmbed(offset, collabNode._xmlElem);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (delCount !== 0) {
							 | 
						||
| 
								 | 
							
								      const childrenToDelete = children.slice(index, index + delCount);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (let i = 0; i < childrenToDelete.length; i++) {
							 | 
						||
| 
								 | 
							
								        childrenToDelete[i].destroy(binding);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (collabNode !== undefined) {
							 | 
						||
| 
								 | 
							
								      children.splice(index, delCount, collabNode);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      children.splice(index, delCount);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  getChildOffset(
							 | 
						||
| 
								 | 
							
								    collabNode:
							 | 
						||
| 
								 | 
							
								      | CollabElementNode
							 | 
						||
| 
								 | 
							
								      | CollabTextNode
							 | 
						||
| 
								 | 
							
								      | CollabDecoratorNode
							 | 
						||
| 
								 | 
							
								      | CollabLineBreakNode,
							 | 
						||
| 
								 | 
							
								  ): number {
							 | 
						||
| 
								 | 
							
								    let offset = 0;
							 | 
						||
| 
								 | 
							
								    const children = this._children;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < children.length; i++) {
							 | 
						||
| 
								 | 
							
								      const child = children[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (child === collabNode) {
							 | 
						||
| 
								 | 
							
								        return offset;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      offset += child.getSize();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  destroy(binding: Binding): void {
							 | 
						||
| 
								 | 
							
								    const collabNodeMap = binding.collabNodeMap;
							 | 
						||
| 
								 | 
							
								    const children = this._children;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < children.length; i++) {
							 | 
						||
| 
								 | 
							
								      children[i].destroy(binding);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    collabNodeMap.delete(this._key);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function $createCollabElementNode(
							 | 
						||
| 
								 | 
							
								  xmlText: XmlText,
							 | 
						||
| 
								 | 
							
								  parent: null | CollabElementNode,
							 | 
						||
| 
								 | 
							
								  type: string,
							 | 
						||
| 
								 | 
							
								): CollabElementNode {
							 | 
						||
| 
								 | 
							
								  const collabNode = new CollabElementNode(xmlText, parent, type);
							 | 
						||
| 
								 | 
							
								  xmlText._collabNode = collabNode;
							 | 
						||
| 
								 | 
							
								  return collabNode;
							 | 
						||
| 
								 | 
							
								}
							 |