-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
operationreplayer.ts
140 lines (117 loc) · 3.66 KB
/
operationreplayer.ts
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module engine/dev-utils/operationreplayer
*/
/* global setTimeout */
import OperationFactory from '../model/operation/operationfactory.js';
import type Model from '../model/model.js';
import type Operation from '../model/operation/operation.js';
/**
* Operation replayer is a development tool created for easy replaying of operations on the document from stringified operations.
*/
export default class OperationReplayer {
private _model: Model;
private _logSeparator: string;
private _operationsToReplay!: Array<Operation>;
/**
* @param model Data model.
* @param logSeparator Separator between operations.
* @param stringifiedOperations Operations to replay.
*/
constructor( model: Model, logSeparator: string, stringifiedOperations: string ) {
this._model = model;
this._logSeparator = logSeparator;
this.setStringifiedOperations( stringifiedOperations );
}
/**
* Parses the given string containing stringified operations and sets parsed operations as operations to replay.
*
* @param stringifiedOperations Stringified operations to replay.
*/
public setStringifiedOperations( stringifiedOperations: string ): void {
if ( stringifiedOperations === '' ) {
this._operationsToReplay = [];
return;
}
this._operationsToReplay = stringifiedOperations
.split( this._logSeparator )
.map( stringifiedOperation => JSON.parse( stringifiedOperation ) );
}
/**
* Returns operations to replay.
*/
public getOperationsToReplay(): Array<Operation> {
return this._operationsToReplay;
}
/**
* Applies all operations with a delay between actions.
*
* @param timeInterval Time between applying operations.
*/
public play( timeInterval = 1000 ): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
const operationReplayer = this;
return new Promise( ( res, rej ) => {
play();
function play() {
operationReplayer.applyNextOperation().then( isFinished => {
if ( isFinished ) {
return res();
}
setTimeout( play, timeInterval );
} ).catch( err => {
rej( err );
} );
}
} );
}
/**
* Applies `numberOfOperations` operations, beginning after the last applied operation (or first, if no operations were applied).
*
* @param numberOfOperations The number of operations to apply.
*/
public applyOperations( numberOfOperations: number ): Promise<void> | undefined {
if ( numberOfOperations <= 0 ) {
return;
}
return this.applyNextOperation()
.then( isFinished => {
if ( !isFinished ) {
return this.applyOperations( numberOfOperations - 1 );
}
} );
}
/**
* Applies all operations to replay at once.
*/
public applyAllOperations(): Promise<void> {
return this.applyNextOperation()
.then( isFinished => {
if ( !isFinished ) {
return this.applyAllOperations();
}
} );
}
/**
* Applies the next operation to replay. Returns a promise with the `isFinished` parameter that is `true` if the last
* operation in the replayer has been applied, `false` otherwise.
*/
public applyNextOperation(): Promise<boolean> {
const model = this._model;
return new Promise( res => {
model.enqueueChange( writer => {
const operationJson = this._operationsToReplay.shift();
if ( !operationJson ) {
return res( true );
}
const operation = OperationFactory.fromJSON( operationJson, model.document );
writer.batch.addOperation( operation );
model.applyOperation( operation );
res( false );
} );
} );
}
}