@ckeditor/ckeditor5-undo 35.2.1 → 35.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +23 -15
- package/src/basecommand.js +162 -203
- package/src/index.js +0 -2
- package/src/redocommand.js +22 -28
- package/src/undo.js +12 -16
- package/src/undocommand.js +24 -38
- package/src/undoediting.js +85 -104
- package/src/undoui.js +46 -59
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ckeditor/ckeditor5-undo",
|
3
|
-
"version": "35.
|
3
|
+
"version": "35.3.0",
|
4
4
|
"description": "Undo feature for CKEditor 5.",
|
5
5
|
"keywords": [
|
6
6
|
"ckeditor",
|
@@ -12,20 +12,23 @@
|
|
12
12
|
],
|
13
13
|
"main": "src/index.js",
|
14
14
|
"dependencies": {
|
15
|
-
"@ckeditor/ckeditor5-core": "^35.
|
16
|
-
"@ckeditor/ckeditor5-engine": "^35.
|
17
|
-
"@ckeditor/ckeditor5-ui": "^35.
|
15
|
+
"@ckeditor/ckeditor5-core": "^35.3.0",
|
16
|
+
"@ckeditor/ckeditor5-engine": "^35.3.0",
|
17
|
+
"@ckeditor/ckeditor5-ui": "^35.3.0"
|
18
18
|
},
|
19
19
|
"devDependencies": {
|
20
|
-
"@ckeditor/ckeditor5-basic-styles": "^35.
|
21
|
-
"@ckeditor/ckeditor5-clipboard": "^35.
|
22
|
-
"@ckeditor/ckeditor5-editor-classic": "^35.
|
23
|
-
"@ckeditor/ckeditor5-enter": "^35.
|
24
|
-
"@ckeditor/ckeditor5-heading": "^35.
|
25
|
-
"@ckeditor/ckeditor5-paragraph": "^35.
|
26
|
-
"@ckeditor/ckeditor5-typing": "^35.
|
27
|
-
"@ckeditor/ckeditor5-table": "^35.
|
28
|
-
"@ckeditor/ckeditor5-utils": "^35.
|
20
|
+
"@ckeditor/ckeditor5-basic-styles": "^35.3.0",
|
21
|
+
"@ckeditor/ckeditor5-clipboard": "^35.3.0",
|
22
|
+
"@ckeditor/ckeditor5-editor-classic": "^35.3.0",
|
23
|
+
"@ckeditor/ckeditor5-enter": "^35.3.0",
|
24
|
+
"@ckeditor/ckeditor5-heading": "^35.3.0",
|
25
|
+
"@ckeditor/ckeditor5-paragraph": "^35.3.0",
|
26
|
+
"@ckeditor/ckeditor5-typing": "^35.3.0",
|
27
|
+
"@ckeditor/ckeditor5-table": "^35.3.0",
|
28
|
+
"@ckeditor/ckeditor5-utils": "^35.3.0",
|
29
|
+
"typescript": "^4.8.4",
|
30
|
+
"webpack": "^5.58.1",
|
31
|
+
"webpack-cli": "^4.9.0"
|
29
32
|
},
|
30
33
|
"engines": {
|
31
34
|
"node": ">=14.0.0",
|
@@ -42,9 +45,14 @@
|
|
42
45
|
},
|
43
46
|
"files": [
|
44
47
|
"lang",
|
45
|
-
"src",
|
48
|
+
"src/**/*.js",
|
49
|
+
"src/**/*.d.ts",
|
46
50
|
"theme",
|
47
51
|
"ckeditor5-metadata.json",
|
48
52
|
"CHANGELOG.md"
|
49
|
-
]
|
53
|
+
],
|
54
|
+
"scripts": {
|
55
|
+
"build": "tsc -p ./tsconfig.release.json",
|
56
|
+
"postversion": "npm run build"
|
57
|
+
}
|
50
58
|
}
|
package/src/basecommand.js
CHANGED
@@ -2,14 +2,11 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/basecommand
|
8
7
|
*/
|
9
|
-
|
10
8
|
import Command from '@ckeditor/ckeditor5-core/src/command';
|
11
9
|
import { transformSets } from '@ckeditor/ckeditor5-engine/src/model/operation/transform';
|
12
|
-
|
13
10
|
/**
|
14
11
|
* Base class for undo feature commands: {@link module:undo/undocommand~UndoCommand} and {@link module:undo/redocommand~RedoCommand}.
|
15
12
|
*
|
@@ -17,210 +14,172 @@ import { transformSets } from '@ckeditor/ckeditor5-engine/src/model/operation/tr
|
|
17
14
|
* @extends module:core/command~Command
|
18
15
|
*/
|
19
16
|
export default class BaseCommand extends Command {
|
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
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
this._createdBatches.add( undoingBatch );
|
170
|
-
|
171
|
-
const operationsToUndo = batchToUndo.operations.slice().filter( operation => operation.isDocumentOperation );
|
172
|
-
operationsToUndo.reverse();
|
173
|
-
|
174
|
-
// We will process each operation from `batchToUndo`, in reverse order. If there were operations A, B and C in undone batch,
|
175
|
-
// we need to revert them in reverse order, so first C' (reversed C), then B', then A'.
|
176
|
-
for ( const operationToUndo of operationsToUndo ) {
|
177
|
-
const nextBaseVersion = operationToUndo.baseVersion + 1;
|
178
|
-
const historyOperations = Array.from( document.history.getOperations( nextBaseVersion ) );
|
179
|
-
|
180
|
-
const transformedSets = transformSets(
|
181
|
-
[ operationToUndo.getReversed() ],
|
182
|
-
historyOperations,
|
183
|
-
{
|
184
|
-
useRelations: true,
|
185
|
-
document: this.editor.model.document,
|
186
|
-
padWithNoOps: false,
|
187
|
-
forceWeakRemove: true
|
188
|
-
}
|
189
|
-
);
|
190
|
-
|
191
|
-
const reversedOperations = transformedSets.operationsA;
|
192
|
-
|
193
|
-
// After reversed operation has been transformed by all history operations, apply it.
|
194
|
-
for ( const operation of reversedOperations ) {
|
195
|
-
// Before applying, add the operation to the `undoingBatch`.
|
196
|
-
undoingBatch.addOperation( operation );
|
197
|
-
model.applyOperation( operation );
|
198
|
-
|
199
|
-
document.history.setOperationAsUndone( operationToUndo, operation );
|
200
|
-
}
|
201
|
-
}
|
202
|
-
}
|
17
|
+
constructor(editor) {
|
18
|
+
super(editor);
|
19
|
+
/**
|
20
|
+
* Stack of items stored by the command. These are pairs of:
|
21
|
+
*
|
22
|
+
* * {@link module:engine/model/batch~Batch batch} saved by the command,
|
23
|
+
* * {@link module:engine/model/selection~Selection selection} state at the moment of saving the batch.
|
24
|
+
*
|
25
|
+
* @protected
|
26
|
+
* @member {Array} #_stack
|
27
|
+
*/
|
28
|
+
this._stack = [];
|
29
|
+
/**
|
30
|
+
* Stores all batches that were created by this command.
|
31
|
+
*
|
32
|
+
* @protected
|
33
|
+
* @member {WeakSet.<module:engine/model/batch~Batch>} #_createdBatches
|
34
|
+
*/
|
35
|
+
this._createdBatches = new WeakSet();
|
36
|
+
// Refresh state, so the command is inactive right after initialization.
|
37
|
+
this.refresh();
|
38
|
+
// Set the transparent batch for the `editor.data.set()` call if the
|
39
|
+
// batch type is not set already.
|
40
|
+
this.listenTo(editor.data, 'set', (evt, data) => {
|
41
|
+
// Create a shallow copy of the options to not change the original args.
|
42
|
+
// And make sure that an object is assigned to data[ 1 ].
|
43
|
+
data[1] = { ...data[1] };
|
44
|
+
const options = data[1];
|
45
|
+
// If batch type is not set, default to non-undoable batch.
|
46
|
+
if (!options.batchType) {
|
47
|
+
options.batchType = { isUndoable: false };
|
48
|
+
}
|
49
|
+
}, { priority: 'high' });
|
50
|
+
// Clear the stack for the `transparent` batches.
|
51
|
+
this.listenTo(editor.data, 'set', (evt, data) => {
|
52
|
+
// We can assume that the object exists and it has a `batchType` property.
|
53
|
+
// It was ensured with a higher priority listener before.
|
54
|
+
const options = data[1];
|
55
|
+
if (!options.batchType.isUndoable) {
|
56
|
+
this.clearStack();
|
57
|
+
}
|
58
|
+
});
|
59
|
+
}
|
60
|
+
/**
|
61
|
+
* @inheritDoc
|
62
|
+
*/
|
63
|
+
refresh() {
|
64
|
+
this.isEnabled = this._stack.length > 0;
|
65
|
+
}
|
66
|
+
/**
|
67
|
+
* Stores a batch in the command, together with the selection state of the {@link module:engine/model/document~Document document}
|
68
|
+
* created by the editor which this command is registered to.
|
69
|
+
*
|
70
|
+
* @param {module:engine/model/batch~Batch} batch The batch to add.
|
71
|
+
*/
|
72
|
+
addBatch(batch) {
|
73
|
+
const docSelection = this.editor.model.document.selection;
|
74
|
+
const selection = {
|
75
|
+
ranges: docSelection.hasOwnRange ? Array.from(docSelection.getRanges()) : [],
|
76
|
+
isBackward: docSelection.isBackward
|
77
|
+
};
|
78
|
+
this._stack.push({ batch, selection });
|
79
|
+
this.refresh();
|
80
|
+
}
|
81
|
+
/**
|
82
|
+
* Removes all items from the stack.
|
83
|
+
*/
|
84
|
+
clearStack() {
|
85
|
+
this._stack = [];
|
86
|
+
this.refresh();
|
87
|
+
}
|
88
|
+
/**
|
89
|
+
* Restores the {@link module:engine/model/document~Document#selection document selection} state after a batch was undone.
|
90
|
+
*
|
91
|
+
* @protected
|
92
|
+
* @param {Array.<module:engine/model/range~Range>} ranges Ranges to be restored.
|
93
|
+
* @param {Boolean} isBackward A flag describing whether the restored range was selected forward or backward.
|
94
|
+
* @param {Array.<module:engine/model/operation/operation~Operation>} operations Operations which has been applied
|
95
|
+
* since selection has been stored.
|
96
|
+
*/
|
97
|
+
_restoreSelection(ranges, isBackward, operations) {
|
98
|
+
const model = this.editor.model;
|
99
|
+
const document = model.document;
|
100
|
+
// This will keep the transformed selection ranges.
|
101
|
+
const selectionRanges = [];
|
102
|
+
// Transform all ranges from the restored selection.
|
103
|
+
const transformedRangeGroups = ranges.map(range => range.getTransformedByOperations(operations));
|
104
|
+
const allRanges = transformedRangeGroups.flat();
|
105
|
+
for (const rangeGroup of transformedRangeGroups) {
|
106
|
+
// While transforming there could appear ranges that are contained by other ranges, we shall ignore them.
|
107
|
+
const transformed = rangeGroup
|
108
|
+
.filter(range => range.root != document.graveyard)
|
109
|
+
.filter(range => !isRangeContainedByAnyOtherRange(range, allRanges));
|
110
|
+
// All the transformed ranges ended up in graveyard.
|
111
|
+
if (!transformed.length) {
|
112
|
+
continue;
|
113
|
+
}
|
114
|
+
// After the range got transformed, we have an array of ranges. Some of those
|
115
|
+
// ranges may be "touching" -- they can be next to each other and could be merged.
|
116
|
+
normalizeRanges(transformed);
|
117
|
+
// For each `range` from `ranges`, we take only one transformed range.
|
118
|
+
// This is because we want to prevent situation where single-range selection
|
119
|
+
// got transformed to multi-range selection.
|
120
|
+
selectionRanges.push(transformed[0]);
|
121
|
+
}
|
122
|
+
// @if CK_DEBUG_ENGINE // console.log( `Restored selection by undo: ${ selectionRanges.join( ', ' ) }` );
|
123
|
+
// `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
|
124
|
+
if (selectionRanges.length) {
|
125
|
+
model.change(writer => {
|
126
|
+
writer.setSelection(selectionRanges, { backward: isBackward });
|
127
|
+
});
|
128
|
+
}
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* Undoes a batch by reversing that batch, transforming reversed batch and finally applying it.
|
132
|
+
* This is a helper method for {@link #execute}.
|
133
|
+
*
|
134
|
+
* @protected
|
135
|
+
* @param {module:engine/model/batch~Batch} batchToUndo The batch to be undone.
|
136
|
+
* @param {module:engine/model/batch~Batch} undoingBatch The batch that will contain undoing changes.
|
137
|
+
*/
|
138
|
+
_undo(batchToUndo, undoingBatch) {
|
139
|
+
const model = this.editor.model;
|
140
|
+
const document = model.document;
|
141
|
+
// All changes done by the command execution will be saved as one batch.
|
142
|
+
this._createdBatches.add(undoingBatch);
|
143
|
+
const operationsToUndo = batchToUndo.operations.slice().filter(operation => operation.isDocumentOperation);
|
144
|
+
operationsToUndo.reverse();
|
145
|
+
// We will process each operation from `batchToUndo`, in reverse order. If there were operations A, B and C in undone batch,
|
146
|
+
// we need to revert them in reverse order, so first C' (reversed C), then B', then A'.
|
147
|
+
for (const operationToUndo of operationsToUndo) {
|
148
|
+
const nextBaseVersion = operationToUndo.baseVersion + 1;
|
149
|
+
const historyOperations = Array.from(document.history.getOperations(nextBaseVersion));
|
150
|
+
const transformedSets = transformSets([operationToUndo.getReversed()], historyOperations, {
|
151
|
+
useRelations: true,
|
152
|
+
document: this.editor.model.document,
|
153
|
+
padWithNoOps: false,
|
154
|
+
forceWeakRemove: true
|
155
|
+
});
|
156
|
+
const reversedOperations = transformedSets.operationsA;
|
157
|
+
// After reversed operation has been transformed by all history operations, apply it.
|
158
|
+
for (const operation of reversedOperations) {
|
159
|
+
// Before applying, add the operation to the `undoingBatch`.
|
160
|
+
undoingBatch.addOperation(operation);
|
161
|
+
model.applyOperation(operation);
|
162
|
+
document.history.setOperationAsUndone(operationToUndo, operation);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
203
166
|
}
|
204
|
-
|
205
167
|
// Normalizes list of ranges by joining intersecting or "touching" ranges.
|
206
168
|
//
|
207
169
|
// @param {Array.<module:engine/model/range~Range>} ranges
|
208
170
|
//
|
209
|
-
function normalizeRanges(
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
}
|
221
|
-
}
|
171
|
+
function normalizeRanges(ranges) {
|
172
|
+
ranges.sort((a, b) => a.start.isBefore(b.start) ? -1 : 1);
|
173
|
+
for (let i = 1; i < ranges.length; i++) {
|
174
|
+
const previousRange = ranges[i - 1];
|
175
|
+
const joinedRange = previousRange.getJoined(ranges[i], true);
|
176
|
+
if (joinedRange) {
|
177
|
+
// Replace the ranges on the list with the new joined range.
|
178
|
+
i--;
|
179
|
+
ranges.splice(i, 2, joinedRange);
|
180
|
+
}
|
181
|
+
}
|
222
182
|
}
|
223
|
-
|
224
|
-
|
225
|
-
return ranges.some( otherRange => otherRange !== range && otherRange.containsRange( range, true ) );
|
183
|
+
function isRangeContainedByAnyOtherRange(range, ranges) {
|
184
|
+
return ranges.some(otherRange => otherRange !== range && otherRange.containsRange(range, true));
|
226
185
|
}
|
package/src/index.js
CHANGED
@@ -2,11 +2,9 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo
|
8
7
|
*/
|
9
|
-
|
10
8
|
export { default as Undo } from './undo';
|
11
9
|
export { default as UndoEditing } from './undoediting';
|
12
10
|
export { default as UndoUi } from './undoui';
|
package/src/redocommand.js
CHANGED
@@ -2,13 +2,10 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/redocommand
|
8
7
|
*/
|
9
|
-
|
10
8
|
import BaseCommand from './basecommand';
|
11
|
-
|
12
9
|
/**
|
13
10
|
* The redo command stores {@link module:engine/model/batch~Batch batches} that were used to undo a batch by
|
14
11
|
* {@link module:undo/undocommand~UndoCommand}. It is able to redo a previously undone batch by reversing the undoing
|
@@ -20,29 +17,26 @@ import BaseCommand from './basecommand';
|
|
20
17
|
* @extends module:undo/basecommand~BaseCommand
|
21
18
|
*/
|
22
19
|
export default class RedoCommand extends BaseCommand {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
this.refresh();
|
47
|
-
}
|
20
|
+
/**
|
21
|
+
* Executes the command. This method reverts the last {@link module:engine/model/batch~Batch batch} added to
|
22
|
+
* the command's stack, applies the reverted and transformed version on the
|
23
|
+
* {@link module:engine/model/document~Document document} and removes the batch from the stack.
|
24
|
+
* Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
|
25
|
+
*
|
26
|
+
* @fires execute
|
27
|
+
*/
|
28
|
+
execute() {
|
29
|
+
const item = this._stack.pop();
|
30
|
+
const redoingBatch = this.editor.model.createBatch({ isUndo: true });
|
31
|
+
// All changes have to be done in one `enqueueChange` callback so other listeners will not step between consecutive
|
32
|
+
// operations, or won't do changes to the document before selection is properly restored.
|
33
|
+
this.editor.model.enqueueChange(redoingBatch, () => {
|
34
|
+
const lastOperation = item.batch.operations[item.batch.operations.length - 1];
|
35
|
+
const nextBaseVersion = lastOperation.baseVersion + 1;
|
36
|
+
const operations = this.editor.model.document.history.getOperations(nextBaseVersion);
|
37
|
+
this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
|
38
|
+
this._undo(item.batch, redoingBatch);
|
39
|
+
});
|
40
|
+
this.refresh();
|
41
|
+
}
|
48
42
|
}
|
package/src/undo.js
CHANGED
@@ -2,15 +2,12 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/undo
|
8
7
|
*/
|
9
|
-
|
10
8
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
11
9
|
import UndoEditing from './undoediting';
|
12
10
|
import UndoUI from './undoui';
|
13
|
-
|
14
11
|
/**
|
15
12
|
* The undo feature.
|
16
13
|
*
|
@@ -103,17 +100,16 @@ import UndoUI from './undoui';
|
|
103
100
|
* @extends module:core/plugin~Plugin
|
104
101
|
*/
|
105
102
|
export default class Undo extends Plugin {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
}
|
103
|
+
/**
|
104
|
+
* @inheritDoc
|
105
|
+
*/
|
106
|
+
static get requires() {
|
107
|
+
return [UndoEditing, UndoUI];
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* @inheritDoc
|
111
|
+
*/
|
112
|
+
static get pluginName() {
|
113
|
+
return 'Undo';
|
114
|
+
}
|
119
115
|
}
|
package/src/undocommand.js
CHANGED
@@ -2,13 +2,10 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/undocommand
|
8
7
|
*/
|
9
|
-
|
10
8
|
import BaseCommand from './basecommand';
|
11
|
-
|
12
9
|
/**
|
13
10
|
* The undo command stores {@link module:engine/model/batch~Batch batches} applied to the
|
14
11
|
* {@link module:engine/model/document~Document document} and is able to undo a batch by reversing it and transforming by
|
@@ -19,39 +16,28 @@ import BaseCommand from './basecommand';
|
|
19
16
|
* @extends module:undo/basecommand~BaseCommand
|
20
17
|
*/
|
21
18
|
export default class UndoCommand extends BaseCommand {
|
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
|
-
this.fire( 'revert', item.batch, undoingBatch );
|
47
|
-
} );
|
48
|
-
|
49
|
-
this.refresh();
|
50
|
-
}
|
19
|
+
/**
|
20
|
+
* Executes the command. This method reverts a {@link module:engine/model/batch~Batch batch} added to the command's stack, transforms
|
21
|
+
* and applies the reverted version on the {@link module:engine/model/document~Document document} and removes the batch from the stack.
|
22
|
+
* Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
|
23
|
+
*
|
24
|
+
* @fires execute
|
25
|
+
* @fires revert
|
26
|
+
* @param {module:engine/model/batch~Batch} [batch] A batch that should be undone. If not set, the last added batch will be undone.
|
27
|
+
*/
|
28
|
+
execute(batch = null) {
|
29
|
+
// If batch is not given, set `batchIndex` to the last index in command stack.
|
30
|
+
const batchIndex = batch ? this._stack.findIndex(a => a.batch == batch) : this._stack.length - 1;
|
31
|
+
const item = this._stack.splice(batchIndex, 1)[0];
|
32
|
+
const undoingBatch = this.editor.model.createBatch({ isUndo: true });
|
33
|
+
// All changes has to be done in one `enqueueChange` callback so other listeners will not
|
34
|
+
// step between consecutive operations, or won't do changes to the document before selection is properly restored.
|
35
|
+
this.editor.model.enqueueChange(undoingBatch, () => {
|
36
|
+
this._undo(item.batch, undoingBatch);
|
37
|
+
const operations = this.editor.model.document.history.getOperations(item.batch.baseVersion);
|
38
|
+
this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
|
39
|
+
this.fire('revert', item.batch, undoingBatch);
|
40
|
+
});
|
41
|
+
this.refresh();
|
42
|
+
}
|
51
43
|
}
|
52
|
-
|
53
|
-
/**
|
54
|
-
* Fired when execution of the command reverts some batch.
|
55
|
-
*
|
56
|
-
* @event revert
|
57
|
-
*/
|
package/src/undoediting.js
CHANGED
@@ -2,15 +2,12 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/undoediting
|
8
7
|
*/
|
9
|
-
|
10
8
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
11
9
|
import UndoCommand from './undocommand';
|
12
10
|
import RedoCommand from './redocommand';
|
13
|
-
|
14
11
|
/**
|
15
12
|
* The undo engine feature.
|
16
13
|
*
|
@@ -19,105 +16,89 @@ import RedoCommand from './redocommand';
|
|
19
16
|
* @extends module:core/plugin~Plugin
|
20
17
|
*/
|
21
18
|
export default class UndoEditing extends Plugin {
|
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
|
-
} else if ( !isUndoBatch ) {
|
108
|
-
// If the batch comes neither from `redoCommand` nor from `undoCommand` then it is a new, regular batch.
|
109
|
-
// Add the batch to the `undoCommand` stack and clear the `redoCommand` stack.
|
110
|
-
this._undoCommand.addBatch( batch );
|
111
|
-
this._redoCommand.clearStack();
|
112
|
-
}
|
113
|
-
}, { priority: 'highest' } );
|
114
|
-
|
115
|
-
this.listenTo( this._undoCommand, 'revert', ( evt, undoneBatch, undoingBatch ) => {
|
116
|
-
this._redoCommand.addBatch( undoingBatch );
|
117
|
-
} );
|
118
|
-
|
119
|
-
editor.keystrokes.set( 'CTRL+Z', 'undo' );
|
120
|
-
editor.keystrokes.set( 'CTRL+Y', 'redo' );
|
121
|
-
editor.keystrokes.set( 'CTRL+SHIFT+Z', 'redo' );
|
122
|
-
}
|
19
|
+
/**
|
20
|
+
* @inheritDoc
|
21
|
+
*/
|
22
|
+
constructor(editor) {
|
23
|
+
super(editor);
|
24
|
+
/**
|
25
|
+
* The command that manages undo {@link module:engine/model/batch~Batch batches} stack (history).
|
26
|
+
* Created and registered during the {@link #init feature initialization}.
|
27
|
+
*
|
28
|
+
* @private
|
29
|
+
* @member {module:undo/undocommand~UndoCommand} #_undoCommand
|
30
|
+
*/
|
31
|
+
/**
|
32
|
+
* The command that manages redo {@link module:engine/model/batch~Batch batches} stack (history).
|
33
|
+
* Created and registered during the {@link #init feature initialization}.
|
34
|
+
*
|
35
|
+
* @private
|
36
|
+
* @member {module:undo/undocommand~UndoCommand} #_redoCommand
|
37
|
+
*/
|
38
|
+
/**
|
39
|
+
* Keeps track of which batches were registered in undo.
|
40
|
+
*
|
41
|
+
* @private
|
42
|
+
* @member {WeakSet.<module:engine/model/batch~Batch>}
|
43
|
+
*/
|
44
|
+
this._batchRegistry = new WeakSet();
|
45
|
+
}
|
46
|
+
/**
|
47
|
+
* @inheritDoc
|
48
|
+
*/
|
49
|
+
static get pluginName() {
|
50
|
+
return 'UndoEditing';
|
51
|
+
}
|
52
|
+
/**
|
53
|
+
* @inheritDoc
|
54
|
+
*/
|
55
|
+
init() {
|
56
|
+
const editor = this.editor;
|
57
|
+
// Create commands.
|
58
|
+
this._undoCommand = new UndoCommand(editor);
|
59
|
+
this._redoCommand = new RedoCommand(editor);
|
60
|
+
// Register command to the editor.
|
61
|
+
editor.commands.add('undo', this._undoCommand);
|
62
|
+
editor.commands.add('redo', this._redoCommand);
|
63
|
+
this.listenTo(editor.model, 'applyOperation', (evt, args) => {
|
64
|
+
const operation = args[0];
|
65
|
+
// Do not register batch if the operation is not a document operation.
|
66
|
+
// This prevents from creating empty undo steps, where all operations where non-document operations.
|
67
|
+
// Non-document operations creates and alters content in detached tree fragments (for example, document fragments).
|
68
|
+
// Most of time this is preparing data before it is inserted into actual tree (for example during copy & paste).
|
69
|
+
// Such operations should not be reversed.
|
70
|
+
if (!operation.isDocumentOperation) {
|
71
|
+
return;
|
72
|
+
}
|
73
|
+
const batch = operation.batch;
|
74
|
+
const isRedoBatch = this._redoCommand._createdBatches.has(batch);
|
75
|
+
const isUndoBatch = this._undoCommand._createdBatches.has(batch);
|
76
|
+
const wasProcessed = this._batchRegistry.has(batch);
|
77
|
+
// Skip the batch if it was already processed.
|
78
|
+
if (wasProcessed) {
|
79
|
+
return;
|
80
|
+
}
|
81
|
+
// Add the batch to the registry so it will not be processed again.
|
82
|
+
this._batchRegistry.add(batch);
|
83
|
+
if (!batch.isUndoable) {
|
84
|
+
return;
|
85
|
+
}
|
86
|
+
if (isRedoBatch) {
|
87
|
+
// If this batch comes from `redoCommand`, add it to the `undoCommand` stack.
|
88
|
+
this._undoCommand.addBatch(batch);
|
89
|
+
}
|
90
|
+
else if (!isUndoBatch) {
|
91
|
+
// If the batch comes neither from `redoCommand` nor from `undoCommand` then it is a new, regular batch.
|
92
|
+
// Add the batch to the `undoCommand` stack and clear the `redoCommand` stack.
|
93
|
+
this._undoCommand.addBatch(batch);
|
94
|
+
this._redoCommand.clearStack();
|
95
|
+
}
|
96
|
+
}, { priority: 'highest' });
|
97
|
+
this.listenTo(this._undoCommand, 'revert', (evt, undoneBatch, undoingBatch) => {
|
98
|
+
this._redoCommand.addBatch(undoingBatch);
|
99
|
+
});
|
100
|
+
editor.keystrokes.set('CTRL+Z', 'undo');
|
101
|
+
editor.keystrokes.set('CTRL+Y', 'redo');
|
102
|
+
editor.keystrokes.set('CTRL+SHIFT+Z', 'redo');
|
103
|
+
}
|
123
104
|
}
|
package/src/undoui.js
CHANGED
@@ -2,76 +2,63 @@
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
4
|
*/
|
5
|
-
|
6
5
|
/**
|
7
6
|
* @module undo/undoui
|
8
7
|
*/
|
9
|
-
|
10
8
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
11
9
|
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
|
12
|
-
|
13
10
|
import undoIcon from '../theme/icons/undo.svg';
|
14
11
|
import redoIcon from '../theme/icons/redo.svg';
|
15
|
-
|
16
12
|
/**
|
17
13
|
* The undo UI feature. It introduces the `'undo'` and `'redo'` buttons to the editor.
|
18
14
|
*
|
19
15
|
* @extends module:core/plugin~Plugin
|
20
16
|
*/
|
21
17
|
export default class UndoUI extends Plugin {
|
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
|
-
this.listenTo( view, 'execute', () => {
|
70
|
-
editor.execute( name );
|
71
|
-
editor.editing.view.focus();
|
72
|
-
} );
|
73
|
-
|
74
|
-
return view;
|
75
|
-
} );
|
76
|
-
}
|
18
|
+
/**
|
19
|
+
* @inheritDoc
|
20
|
+
*/
|
21
|
+
static get pluginName() {
|
22
|
+
return 'UndoUI';
|
23
|
+
}
|
24
|
+
/**
|
25
|
+
* @inheritDoc
|
26
|
+
*/
|
27
|
+
init() {
|
28
|
+
const editor = this.editor;
|
29
|
+
const locale = editor.locale;
|
30
|
+
const t = editor.t;
|
31
|
+
const localizedUndoIcon = locale.uiLanguageDirection == 'ltr' ? undoIcon : redoIcon;
|
32
|
+
const localizedRedoIcon = locale.uiLanguageDirection == 'ltr' ? redoIcon : undoIcon;
|
33
|
+
this._addButton('undo', t('Undo'), 'CTRL+Z', localizedUndoIcon);
|
34
|
+
this._addButton('redo', t('Redo'), 'CTRL+Y', localizedRedoIcon);
|
35
|
+
}
|
36
|
+
/**
|
37
|
+
* Creates a button for the specified command.
|
38
|
+
*
|
39
|
+
* @private
|
40
|
+
* @param {String} name Command name.
|
41
|
+
* @param {String} label Button label.
|
42
|
+
* @param {String} keystroke Command keystroke.
|
43
|
+
* @param {String} Icon Source of the icon.
|
44
|
+
*/
|
45
|
+
_addButton(name, label, keystroke, Icon) {
|
46
|
+
const editor = this.editor;
|
47
|
+
editor.ui.componentFactory.add(name, locale => {
|
48
|
+
const command = editor.commands.get(name);
|
49
|
+
const view = new ButtonView(locale);
|
50
|
+
view.set({
|
51
|
+
label,
|
52
|
+
icon: Icon,
|
53
|
+
keystroke,
|
54
|
+
tooltip: true
|
55
|
+
});
|
56
|
+
view.bind('isEnabled').to(command, 'isEnabled');
|
57
|
+
this.listenTo(view, 'execute', () => {
|
58
|
+
editor.execute(name);
|
59
|
+
editor.editing.view.focus();
|
60
|
+
});
|
61
|
+
return view;
|
62
|
+
});
|
63
|
+
}
|
77
64
|
}
|