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

96% Statements 24/25
87.5% Branches 14/16
100% Functions 1/1
95.83% Lines 23/24

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 791x           1x 1x                     1x 868x 217x   217x   216x 216x     216x 2x 2x 1x 1x 1x                       1x     1x                         216x             216x 2x     216x 216x     217x    
import { findClosestEntityWrapper, parseEntityFormat } from 'roosterjs-content-model-dom';
import type {
    AddUndoSnapshot,
    EntityOperationEvent,
    Snapshot,
} from 'roosterjs-content-model-types';
import { createSnapshotSelection } from './createSnapshotSelection';
import { getPath } from './getPath';
 
/**
 * @internal
 * Add an undo snapshot to current undo snapshot stack
 * @param core The EditorCore object
 * @param canUndoByBackspace True if this action can be undone when user press Backspace key (aka Auto Complete).
 * @param entityStates @optional Entity states related to this snapshot.
 * Each entity state will cause an EntityOperation event with operation = EntityOperation.UpdateEntityState
 * when undo/redo to this snapshot
 */
export const addUndoSnapshot: AddUndoSnapshot = (core, canUndoByBackspace, entityStates) => {
    const { lifecycle, physicalRoot, logicalRoot, undo } = core;
    let snapshot: Snapshot | null = null;
 
    if (!lifecycle.shadowEditFragment) {
        // Need to create snapshot selection before retrieve innerHTML since HTML can be changed during creating selection when normalize table
        const selection = createSnapshotSelection(core);
        const html = physicalRoot.innerHTML;
 
        // Give plugins the chance to share entity states to include in the snapshot if the logical root is in an entity
        if (logicalRoot !== physicalRoot) {
            const entityWrapper = findClosestEntityWrapper(logicalRoot, core.domHelper);
            if (!entityStates && entityWrapper) {
                const entityFormat = parseEntityFormat(entityWrapper);
                Eif (entityFormat.entityType && entityFormat.id) {
                    const event = <EntityOperationEvent>{
                        eventType: 'entityOperation',
                        operation: 'snapshotEntityState',
                        entity: {
                            type: entityFormat.entityType,
                            id: entityFormat.id,
                            wrapper: entityWrapper,
                            isReadonly: entityFormat.isReadonly,
                        },
                        state: undefined,
                    };
 
                    core.api.triggerEvent(core, event, false);
 
                    // Copy out any entity states from the plugins
                    Iif (event.state) {
                        entityStates = [
                            {
                                type: entityFormat.entityType,
                                id: entityFormat.id,
                                state: event.state,
                            },
                        ];
                    }
                }
            }
        }
 
        snapshot = {
            html,
            entityStates,
            isDarkMode: !!lifecycle.isDarkMode,
            selection,
        };
 
        if (logicalRoot !== physicalRoot) {
            snapshot.logicalRootPath = getPath(logicalRoot, 0, physicalRoot);
        }
 
        undo.snapshotsManager.addSnapshot(snapshot, !!canUndoByBackspace);
        undo.snapshotsManager.hasNewContent = false;
    }
 
    return snapshot;
};