@ckeditor/ckeditor5-undo 41.3.1 → 41.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index-content.css +4 -0
- package/dist/index-editor.css +4 -0
- package/dist/index.css +4 -0
- package/dist/index.js +410 -0
- package/dist/index.js.map +1 -0
- package/dist/translations/ar.d.ts +8 -0
- package/dist/translations/ar.js +5 -0
- package/dist/translations/ar.umd.js +11 -0
- package/dist/translations/ast.d.ts +8 -0
- package/dist/translations/ast.js +5 -0
- package/dist/translations/ast.umd.js +11 -0
- package/dist/translations/az.d.ts +8 -0
- package/dist/translations/az.js +5 -0
- package/dist/translations/az.umd.js +11 -0
- package/dist/translations/bg.d.ts +8 -0
- package/dist/translations/bg.js +5 -0
- package/dist/translations/bg.umd.js +11 -0
- package/dist/translations/bn.d.ts +8 -0
- package/dist/translations/bn.js +5 -0
- package/dist/translations/bn.umd.js +11 -0
- package/dist/translations/ca.d.ts +8 -0
- package/dist/translations/ca.js +5 -0
- package/dist/translations/ca.umd.js +11 -0
- package/dist/translations/cs.d.ts +8 -0
- package/dist/translations/cs.js +5 -0
- package/dist/translations/cs.umd.js +11 -0
- package/dist/translations/da.d.ts +8 -0
- package/dist/translations/da.js +5 -0
- package/dist/translations/da.umd.js +11 -0
- package/dist/translations/de-ch.d.ts +8 -0
- package/dist/translations/de-ch.js +5 -0
- package/dist/translations/de-ch.umd.js +11 -0
- package/dist/translations/de.d.ts +8 -0
- package/dist/translations/de.js +5 -0
- package/dist/translations/de.umd.js +11 -0
- package/dist/translations/el.d.ts +8 -0
- package/dist/translations/el.js +5 -0
- package/dist/translations/el.umd.js +11 -0
- package/dist/translations/en-au.d.ts +8 -0
- package/dist/translations/en-au.js +5 -0
- package/dist/translations/en-au.umd.js +11 -0
- package/dist/translations/en-gb.d.ts +8 -0
- package/dist/translations/en-gb.js +5 -0
- package/dist/translations/en-gb.umd.js +11 -0
- package/dist/translations/en.d.ts +8 -0
- package/dist/translations/en.js +5 -0
- package/dist/translations/en.umd.js +11 -0
- package/dist/translations/eo.d.ts +8 -0
- package/dist/translations/eo.js +5 -0
- package/dist/translations/eo.umd.js +11 -0
- package/dist/translations/es.d.ts +8 -0
- package/dist/translations/es.js +5 -0
- package/dist/translations/es.umd.js +11 -0
- package/dist/translations/et.d.ts +8 -0
- package/dist/translations/et.js +5 -0
- package/dist/translations/et.umd.js +11 -0
- package/dist/translations/eu.d.ts +8 -0
- package/dist/translations/eu.js +5 -0
- package/dist/translations/eu.umd.js +11 -0
- package/dist/translations/fa.d.ts +8 -0
- package/dist/translations/fa.js +5 -0
- package/dist/translations/fa.umd.js +11 -0
- package/dist/translations/fi.d.ts +8 -0
- package/dist/translations/fi.js +5 -0
- package/dist/translations/fi.umd.js +11 -0
- package/dist/translations/fr.d.ts +8 -0
- package/dist/translations/fr.js +5 -0
- package/dist/translations/fr.umd.js +11 -0
- package/dist/translations/gl.d.ts +8 -0
- package/dist/translations/gl.js +5 -0
- package/dist/translations/gl.umd.js +11 -0
- package/dist/translations/he.d.ts +8 -0
- package/dist/translations/he.js +5 -0
- package/dist/translations/he.umd.js +11 -0
- package/dist/translations/hi.d.ts +8 -0
- package/dist/translations/hi.js +5 -0
- package/dist/translations/hi.umd.js +11 -0
- package/dist/translations/hr.d.ts +8 -0
- package/dist/translations/hr.js +5 -0
- package/dist/translations/hr.umd.js +11 -0
- package/dist/translations/hu.d.ts +8 -0
- package/dist/translations/hu.js +5 -0
- package/dist/translations/hu.umd.js +11 -0
- package/dist/translations/id.d.ts +8 -0
- package/dist/translations/id.js +5 -0
- package/dist/translations/id.umd.js +11 -0
- package/dist/translations/it.d.ts +8 -0
- package/dist/translations/it.js +5 -0
- package/dist/translations/it.umd.js +11 -0
- package/dist/translations/ja.d.ts +8 -0
- package/dist/translations/ja.js +5 -0
- package/dist/translations/ja.umd.js +11 -0
- package/dist/translations/km.d.ts +8 -0
- package/dist/translations/km.js +5 -0
- package/dist/translations/km.umd.js +11 -0
- package/dist/translations/kn.d.ts +8 -0
- package/dist/translations/kn.js +5 -0
- package/dist/translations/kn.umd.js +11 -0
- package/dist/translations/ko.d.ts +8 -0
- package/dist/translations/ko.js +5 -0
- package/dist/translations/ko.umd.js +11 -0
- package/dist/translations/ku.d.ts +8 -0
- package/dist/translations/ku.js +5 -0
- package/dist/translations/ku.umd.js +11 -0
- package/dist/translations/lt.d.ts +8 -0
- package/dist/translations/lt.js +5 -0
- package/dist/translations/lt.umd.js +11 -0
- package/dist/translations/lv.d.ts +8 -0
- package/dist/translations/lv.js +5 -0
- package/dist/translations/lv.umd.js +11 -0
- package/dist/translations/ms.d.ts +8 -0
- package/dist/translations/ms.js +5 -0
- package/dist/translations/ms.umd.js +11 -0
- package/dist/translations/nb.d.ts +8 -0
- package/dist/translations/nb.js +5 -0
- package/dist/translations/nb.umd.js +11 -0
- package/dist/translations/ne.d.ts +8 -0
- package/dist/translations/ne.js +5 -0
- package/dist/translations/ne.umd.js +11 -0
- package/dist/translations/nl.d.ts +8 -0
- package/dist/translations/nl.js +5 -0
- package/dist/translations/nl.umd.js +11 -0
- package/dist/translations/no.d.ts +8 -0
- package/dist/translations/no.js +5 -0
- package/dist/translations/no.umd.js +11 -0
- package/dist/translations/pl.d.ts +8 -0
- package/dist/translations/pl.js +5 -0
- package/dist/translations/pl.umd.js +11 -0
- package/dist/translations/pt-br.d.ts +8 -0
- package/dist/translations/pt-br.js +5 -0
- package/dist/translations/pt-br.umd.js +11 -0
- package/dist/translations/pt.d.ts +8 -0
- package/dist/translations/pt.js +5 -0
- package/dist/translations/pt.umd.js +11 -0
- package/dist/translations/ro.d.ts +8 -0
- package/dist/translations/ro.js +5 -0
- package/dist/translations/ro.umd.js +11 -0
- package/dist/translations/ru.d.ts +8 -0
- package/dist/translations/ru.js +5 -0
- package/dist/translations/ru.umd.js +11 -0
- package/dist/translations/si.d.ts +8 -0
- package/dist/translations/si.js +5 -0
- package/dist/translations/si.umd.js +11 -0
- package/dist/translations/sk.d.ts +8 -0
- package/dist/translations/sk.js +5 -0
- package/dist/translations/sk.umd.js +11 -0
- package/dist/translations/sq.d.ts +8 -0
- package/dist/translations/sq.js +5 -0
- package/dist/translations/sq.umd.js +11 -0
- package/dist/translations/sr-latn.d.ts +8 -0
- package/dist/translations/sr-latn.js +5 -0
- package/dist/translations/sr-latn.umd.js +11 -0
- package/dist/translations/sr.d.ts +8 -0
- package/dist/translations/sr.js +5 -0
- package/dist/translations/sr.umd.js +11 -0
- package/dist/translations/sv.d.ts +8 -0
- package/dist/translations/sv.js +5 -0
- package/dist/translations/sv.umd.js +11 -0
- package/dist/translations/th.d.ts +8 -0
- package/dist/translations/th.js +5 -0
- package/dist/translations/th.umd.js +11 -0
- package/dist/translations/ti.d.ts +8 -0
- package/dist/translations/ti.js +5 -0
- package/dist/translations/ti.umd.js +11 -0
- package/dist/translations/tk.d.ts +8 -0
- package/dist/translations/tk.js +5 -0
- package/dist/translations/tk.umd.js +11 -0
- package/dist/translations/tr.d.ts +8 -0
- package/dist/translations/tr.js +5 -0
- package/dist/translations/tr.umd.js +11 -0
- package/dist/translations/tt.d.ts +8 -0
- package/dist/translations/tt.js +5 -0
- package/dist/translations/tt.umd.js +11 -0
- package/dist/translations/ug.d.ts +8 -0
- package/dist/translations/ug.js +5 -0
- package/dist/translations/ug.umd.js +11 -0
- package/dist/translations/uk.d.ts +8 -0
- package/dist/translations/uk.js +5 -0
- package/dist/translations/uk.umd.js +11 -0
- package/dist/translations/ur.d.ts +8 -0
- package/dist/translations/ur.js +5 -0
- package/dist/translations/ur.umd.js +11 -0
- package/dist/translations/uz.d.ts +8 -0
- package/dist/translations/uz.js +5 -0
- package/dist/translations/uz.umd.js +11 -0
- package/dist/translations/vi.d.ts +8 -0
- package/dist/translations/vi.js +5 -0
- package/dist/translations/vi.umd.js +11 -0
- package/dist/translations/zh-cn.d.ts +8 -0
- package/dist/translations/zh-cn.js +5 -0
- package/dist/translations/zh-cn.umd.js +11 -0
- package/dist/translations/zh.d.ts +8 -0
- package/dist/translations/zh.js +5 -0
- package/dist/translations/zh.umd.js +11 -0
- package/dist/types/augmentation.d.ts +20 -0
- package/dist/types/basecommand.d.ts +76 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/redocommand.d.ts +31 -0
- package/dist/types/undo.d.ts +121 -0
- package/dist/types/undocommand.d.ts +41 -0
- package/dist/types/undoediting.d.ts +41 -0
- package/dist/types/undoui.d.ts +38 -0
- package/lang/translations/ti.po +26 -0
- package/package.json +5 -4
package/dist/index.css
ADDED
package/dist/index.js
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
|
+
*/
|
5
|
+
import { Command, Plugin, icons } from '@ckeditor/ckeditor5-core/dist/index.js';
|
6
|
+
import { transformSets, NoOperation } from '@ckeditor/ckeditor5-engine/dist/index.js';
|
7
|
+
import { ButtonView, MenuBarMenuListItemButtonView } from '@ckeditor/ckeditor5-ui/dist/index.js';
|
8
|
+
|
9
|
+
class BaseCommand extends Command {
|
10
|
+
/**
|
11
|
+
* @inheritDoc
|
12
|
+
*/ refresh() {
|
13
|
+
this.isEnabled = this._stack.length > 0;
|
14
|
+
}
|
15
|
+
/**
|
16
|
+
* Returns all batches created by this command.
|
17
|
+
*/ get createdBatches() {
|
18
|
+
return this._createdBatches;
|
19
|
+
}
|
20
|
+
/**
|
21
|
+
* Stores a batch in the command, together with the selection state of the {@link module:engine/model/document~Document document}
|
22
|
+
* created by the editor which this command is registered to.
|
23
|
+
*
|
24
|
+
* @param batch The batch to add.
|
25
|
+
*/ addBatch(batch) {
|
26
|
+
const docSelection = this.editor.model.document.selection;
|
27
|
+
const selection = {
|
28
|
+
ranges: docSelection.hasOwnRange ? Array.from(docSelection.getRanges()) : [],
|
29
|
+
isBackward: docSelection.isBackward
|
30
|
+
};
|
31
|
+
this._stack.push({
|
32
|
+
batch,
|
33
|
+
selection
|
34
|
+
});
|
35
|
+
this.refresh();
|
36
|
+
}
|
37
|
+
/**
|
38
|
+
* Removes all items from the stack.
|
39
|
+
*/ clearStack() {
|
40
|
+
this._stack = [];
|
41
|
+
this.refresh();
|
42
|
+
}
|
43
|
+
/**
|
44
|
+
* Restores the {@link module:engine/model/document~Document#selection document selection} state after a batch was undone.
|
45
|
+
*
|
46
|
+
* @param ranges Ranges to be restored.
|
47
|
+
* @param isBackward A flag describing whether the restored range was selected forward or backward.
|
48
|
+
* @param operations Operations which has been applied since selection has been stored.
|
49
|
+
*/ _restoreSelection(ranges, isBackward, operations) {
|
50
|
+
const model = this.editor.model;
|
51
|
+
const document = model.document;
|
52
|
+
// This will keep the transformed selection ranges.
|
53
|
+
const selectionRanges = [];
|
54
|
+
// Transform all ranges from the restored selection.
|
55
|
+
const transformedRangeGroups = ranges.map((range)=>range.getTransformedByOperations(operations));
|
56
|
+
const allRanges = transformedRangeGroups.flat();
|
57
|
+
for (const rangeGroup of transformedRangeGroups){
|
58
|
+
// While transforming there could appear ranges that are contained by other ranges, we shall ignore them.
|
59
|
+
const transformed = rangeGroup.filter((range)=>range.root != document.graveyard).filter((range)=>!isRangeContainedByAnyOtherRange(range, allRanges));
|
60
|
+
// All the transformed ranges ended up in graveyard.
|
61
|
+
if (!transformed.length) {
|
62
|
+
continue;
|
63
|
+
}
|
64
|
+
// After the range got transformed, we have an array of ranges. Some of those
|
65
|
+
// ranges may be "touching" -- they can be next to each other and could be merged.
|
66
|
+
normalizeRanges(transformed);
|
67
|
+
// For each `range` from `ranges`, we take only one transformed range.
|
68
|
+
// This is because we want to prevent situation where single-range selection
|
69
|
+
// got transformed to multi-range selection.
|
70
|
+
selectionRanges.push(transformed[0]);
|
71
|
+
}
|
72
|
+
// @if CK_DEBUG_ENGINE // console.log( `Restored selection by undo: ${ selectionRanges.join( ', ' ) }` );
|
73
|
+
// `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
|
74
|
+
if (selectionRanges.length) {
|
75
|
+
model.change((writer)=>{
|
76
|
+
writer.setSelection(selectionRanges, {
|
77
|
+
backward: isBackward
|
78
|
+
});
|
79
|
+
});
|
80
|
+
}
|
81
|
+
}
|
82
|
+
/**
|
83
|
+
* Undoes a batch by reversing that batch, transforming reversed batch and finally applying it.
|
84
|
+
* This is a helper method for {@link #execute}.
|
85
|
+
*
|
86
|
+
* @param batchToUndo The batch to be undone.
|
87
|
+
* @param undoingBatch The batch that will contain undoing changes.
|
88
|
+
*/ _undo(batchToUndo, undoingBatch) {
|
89
|
+
const model = this.editor.model;
|
90
|
+
const document = model.document;
|
91
|
+
// All changes done by the command execution will be saved as one batch.
|
92
|
+
this._createdBatches.add(undoingBatch);
|
93
|
+
const operationsToUndo = batchToUndo.operations.slice().filter((operation)=>operation.isDocumentOperation);
|
94
|
+
operationsToUndo.reverse();
|
95
|
+
// We will process each operation from `batchToUndo`, in reverse order. If there were operations A, B and C in undone batch,
|
96
|
+
// we need to revert them in reverse order, so first C' (reversed C), then B', then A'.
|
97
|
+
for (const operationToUndo of operationsToUndo){
|
98
|
+
const nextBaseVersion = operationToUndo.baseVersion + 1;
|
99
|
+
const historyOperations = Array.from(document.history.getOperations(nextBaseVersion));
|
100
|
+
const transformedSets = transformSets([
|
101
|
+
operationToUndo.getReversed()
|
102
|
+
], historyOperations, {
|
103
|
+
useRelations: true,
|
104
|
+
document: this.editor.model.document,
|
105
|
+
padWithNoOps: false,
|
106
|
+
forceWeakRemove: true
|
107
|
+
});
|
108
|
+
const reversedOperations = transformedSets.operationsA;
|
109
|
+
// After reversed operation has been transformed by all history operations, apply it.
|
110
|
+
for (let operation of reversedOperations){
|
111
|
+
// Do not apply any operation on non-editable space.
|
112
|
+
const affectedSelectable = operation.affectedSelectable;
|
113
|
+
if (affectedSelectable && !model.canEditAt(affectedSelectable)) {
|
114
|
+
operation = new NoOperation(operation.baseVersion);
|
115
|
+
}
|
116
|
+
// Before applying, add the operation to the `undoingBatch`.
|
117
|
+
undoingBatch.addOperation(operation);
|
118
|
+
model.applyOperation(operation);
|
119
|
+
document.history.setOperationAsUndone(operationToUndo, operation);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
/**
|
124
|
+
* @inheritDoc
|
125
|
+
*/ constructor(editor){
|
126
|
+
super(editor);
|
127
|
+
/**
|
128
|
+
* Stack of items stored by the command. These are pairs of:
|
129
|
+
*
|
130
|
+
* * {@link module:engine/model/batch~Batch batch} saved by the command,
|
131
|
+
* * {@link module:engine/model/selection~Selection selection} state at the moment of saving the batch.
|
132
|
+
*/ this._stack = [];
|
133
|
+
/**
|
134
|
+
* Stores all batches that were created by this command.
|
135
|
+
*
|
136
|
+
* @internal
|
137
|
+
*/ this._createdBatches = new WeakSet();
|
138
|
+
// Refresh state, so the command is inactive right after initialization.
|
139
|
+
this.refresh();
|
140
|
+
// This command should not depend on selection change.
|
141
|
+
this._isEnabledBasedOnSelection = false;
|
142
|
+
// Set the transparent batch for the `editor.data.set()` call if the
|
143
|
+
// batch type is not set already.
|
144
|
+
this.listenTo(editor.data, 'set', (evt, data)=>{
|
145
|
+
// Create a shallow copy of the options to not change the original args.
|
146
|
+
// And make sure that an object is assigned to data[ 1 ].
|
147
|
+
data[1] = {
|
148
|
+
...data[1]
|
149
|
+
};
|
150
|
+
const options = data[1];
|
151
|
+
// If batch type is not set, default to non-undoable batch.
|
152
|
+
if (!options.batchType) {
|
153
|
+
options.batchType = {
|
154
|
+
isUndoable: false
|
155
|
+
};
|
156
|
+
}
|
157
|
+
}, {
|
158
|
+
priority: 'high'
|
159
|
+
});
|
160
|
+
// Clear the stack for the `transparent` batches.
|
161
|
+
this.listenTo(editor.data, 'set', (evt, data)=>{
|
162
|
+
// We can assume that the object exists and it has a `batchType` property.
|
163
|
+
// It was ensured with a higher priority listener before.
|
164
|
+
const options = data[1];
|
165
|
+
if (!options.batchType.isUndoable) {
|
166
|
+
this.clearStack();
|
167
|
+
}
|
168
|
+
});
|
169
|
+
}
|
170
|
+
}
|
171
|
+
/**
|
172
|
+
* Normalizes list of ranges by joining intersecting or "touching" ranges.
|
173
|
+
*
|
174
|
+
* @param ranges Ranges to be normalized.
|
175
|
+
*/ function normalizeRanges(ranges) {
|
176
|
+
ranges.sort((a, b)=>a.start.isBefore(b.start) ? -1 : 1);
|
177
|
+
for(let i = 1; i < ranges.length; i++){
|
178
|
+
const previousRange = ranges[i - 1];
|
179
|
+
const joinedRange = previousRange.getJoined(ranges[i], true);
|
180
|
+
if (joinedRange) {
|
181
|
+
// Replace the ranges on the list with the new joined range.
|
182
|
+
i--;
|
183
|
+
ranges.splice(i, 2, joinedRange);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
function isRangeContainedByAnyOtherRange(range, ranges) {
|
188
|
+
return ranges.some((otherRange)=>otherRange !== range && otherRange.containsRange(range, true));
|
189
|
+
}
|
190
|
+
|
191
|
+
class UndoCommand extends BaseCommand {
|
192
|
+
/**
|
193
|
+
* Executes the command. This method reverts a {@link module:engine/model/batch~Batch batch} added to the command's stack, transforms
|
194
|
+
* and applies the reverted version on the {@link module:engine/model/document~Document document} and removes the batch from the stack.
|
195
|
+
* Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
|
196
|
+
*
|
197
|
+
* @fires execute
|
198
|
+
* @fires revert
|
199
|
+
* @param batch A batch that should be undone. If not set, the last added batch will be undone.
|
200
|
+
*/ execute(batch = null) {
|
201
|
+
// If batch is not given, set `batchIndex` to the last index in command stack.
|
202
|
+
const batchIndex = batch ? this._stack.findIndex((a)=>a.batch == batch) : this._stack.length - 1;
|
203
|
+
const item = this._stack.splice(batchIndex, 1)[0];
|
204
|
+
const undoingBatch = this.editor.model.createBatch({
|
205
|
+
isUndo: true
|
206
|
+
});
|
207
|
+
// All changes have to be done in one `enqueueChange` callback so other listeners will not
|
208
|
+
// step between consecutive operations, or won't do changes to the document before selection is properly restored.
|
209
|
+
this.editor.model.enqueueChange(undoingBatch, ()=>{
|
210
|
+
this._undo(item.batch, undoingBatch);
|
211
|
+
const operations = this.editor.model.document.history.getOperations(item.batch.baseVersion);
|
212
|
+
this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
|
213
|
+
});
|
214
|
+
// Firing `revert` event after the change block to make sure that it includes all changes from post-fixers
|
215
|
+
// and make sure that the selection is "stabilized" (the selection range is saved after undo is executed and then
|
216
|
+
// restored on redo, so it is important that the selection range is saved after post-fixers are done).
|
217
|
+
this.fire('revert', item.batch, undoingBatch);
|
218
|
+
this.refresh();
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
class RedoCommand extends BaseCommand {
|
223
|
+
/**
|
224
|
+
* Executes the command. This method reverts the last {@link module:engine/model/batch~Batch batch} added to
|
225
|
+
* the command's stack, applies the reverted and transformed version on the
|
226
|
+
* {@link module:engine/model/document~Document document} and removes the batch from the stack.
|
227
|
+
* Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
|
228
|
+
*
|
229
|
+
* @fires execute
|
230
|
+
*/ execute() {
|
231
|
+
const item = this._stack.pop();
|
232
|
+
const redoingBatch = this.editor.model.createBatch({
|
233
|
+
isUndo: true
|
234
|
+
});
|
235
|
+
// All changes have to be done in one `enqueueChange` callback so other listeners will not step between consecutive
|
236
|
+
// operations, or won't do changes to the document before selection is properly restored.
|
237
|
+
this.editor.model.enqueueChange(redoingBatch, ()=>{
|
238
|
+
const lastOperation = item.batch.operations[item.batch.operations.length - 1];
|
239
|
+
const nextBaseVersion = lastOperation.baseVersion + 1;
|
240
|
+
const operations = this.editor.model.document.history.getOperations(nextBaseVersion);
|
241
|
+
this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
|
242
|
+
this._undo(item.batch, redoingBatch);
|
243
|
+
});
|
244
|
+
this.refresh();
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
class UndoEditing extends Plugin {
|
249
|
+
/**
|
250
|
+
* @inheritDoc
|
251
|
+
*/ static get pluginName() {
|
252
|
+
return 'UndoEditing';
|
253
|
+
}
|
254
|
+
/**
|
255
|
+
* @inheritDoc
|
256
|
+
*/ init() {
|
257
|
+
const editor = this.editor;
|
258
|
+
const t = editor.t;
|
259
|
+
// Create commands.
|
260
|
+
this._undoCommand = new UndoCommand(editor);
|
261
|
+
this._redoCommand = new RedoCommand(editor);
|
262
|
+
// Register command to the editor.
|
263
|
+
editor.commands.add('undo', this._undoCommand);
|
264
|
+
editor.commands.add('redo', this._redoCommand);
|
265
|
+
this.listenTo(editor.model, 'applyOperation', (evt, args)=>{
|
266
|
+
const operation = args[0];
|
267
|
+
// Do not register batch if the operation is not a document operation.
|
268
|
+
// This prevents from creating empty undo steps, where all operations where non-document operations.
|
269
|
+
// Non-document operations creates and alters content in detached tree fragments (for example, document fragments).
|
270
|
+
// Most of time this is preparing data before it is inserted into actual tree (for example during copy & paste).
|
271
|
+
// Such operations should not be reversed.
|
272
|
+
if (!operation.isDocumentOperation) {
|
273
|
+
return;
|
274
|
+
}
|
275
|
+
const batch = operation.batch;
|
276
|
+
const isRedoBatch = this._redoCommand.createdBatches.has(batch);
|
277
|
+
const isUndoBatch = this._undoCommand.createdBatches.has(batch);
|
278
|
+
const wasProcessed = this._batchRegistry.has(batch);
|
279
|
+
// Skip the batch if it was already processed.
|
280
|
+
if (wasProcessed) {
|
281
|
+
return;
|
282
|
+
}
|
283
|
+
// Add the batch to the registry so it will not be processed again.
|
284
|
+
this._batchRegistry.add(batch);
|
285
|
+
if (!batch.isUndoable) {
|
286
|
+
return;
|
287
|
+
}
|
288
|
+
if (isRedoBatch) {
|
289
|
+
// If this batch comes from `redoCommand`, add it to the `undoCommand` stack.
|
290
|
+
this._undoCommand.addBatch(batch);
|
291
|
+
} else if (!isUndoBatch) {
|
292
|
+
// If the batch comes neither from `redoCommand` nor from `undoCommand` then it is a new, regular batch.
|
293
|
+
// Add the batch to the `undoCommand` stack and clear the `redoCommand` stack.
|
294
|
+
this._undoCommand.addBatch(batch);
|
295
|
+
this._redoCommand.clearStack();
|
296
|
+
}
|
297
|
+
}, {
|
298
|
+
priority: 'highest'
|
299
|
+
});
|
300
|
+
this.listenTo(this._undoCommand, 'revert', (evt, undoneBatch, undoingBatch)=>{
|
301
|
+
this._redoCommand.addBatch(undoingBatch);
|
302
|
+
});
|
303
|
+
editor.keystrokes.set('CTRL+Z', 'undo');
|
304
|
+
editor.keystrokes.set('CTRL+Y', 'redo');
|
305
|
+
editor.keystrokes.set('CTRL+SHIFT+Z', 'redo');
|
306
|
+
// Add the information about the keystrokes to the accessibility database.
|
307
|
+
editor.accessibility.addKeystrokeInfos({
|
308
|
+
keystrokes: [
|
309
|
+
{
|
310
|
+
label: t('Undo'),
|
311
|
+
keystroke: 'CTRL+Z'
|
312
|
+
},
|
313
|
+
{
|
314
|
+
label: t('Redo'),
|
315
|
+
keystroke: [
|
316
|
+
[
|
317
|
+
'CTRL+Y'
|
318
|
+
],
|
319
|
+
[
|
320
|
+
'CTRL+SHIFT+Z'
|
321
|
+
]
|
322
|
+
]
|
323
|
+
}
|
324
|
+
]
|
325
|
+
});
|
326
|
+
}
|
327
|
+
constructor(){
|
328
|
+
super(...arguments);
|
329
|
+
/**
|
330
|
+
* Keeps track of which batches were registered in undo.
|
331
|
+
*/ this._batchRegistry = new WeakSet();
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
class UndoUI extends Plugin {
|
336
|
+
/**
|
337
|
+
* @inheritDoc
|
338
|
+
*/ static get pluginName() {
|
339
|
+
return 'UndoUI';
|
340
|
+
}
|
341
|
+
/**
|
342
|
+
* @inheritDoc
|
343
|
+
*/ init() {
|
344
|
+
const editor = this.editor;
|
345
|
+
const locale = editor.locale;
|
346
|
+
const t = editor.t;
|
347
|
+
const localizedUndoIcon = locale.uiLanguageDirection == 'ltr' ? icons.undo : icons.redo;
|
348
|
+
const localizedRedoIcon = locale.uiLanguageDirection == 'ltr' ? icons.redo : icons.undo;
|
349
|
+
this._addButtonsToFactory('undo', t('Undo'), 'CTRL+Z', localizedUndoIcon);
|
350
|
+
this._addButtonsToFactory('redo', t('Redo'), 'CTRL+Y', localizedRedoIcon);
|
351
|
+
}
|
352
|
+
/**
|
353
|
+
* Creates a button for the specified command.
|
354
|
+
*
|
355
|
+
* @param name Command name.
|
356
|
+
* @param label Button label.
|
357
|
+
* @param keystroke Command keystroke.
|
358
|
+
* @param Icon Source of the icon.
|
359
|
+
*/ _addButtonsToFactory(name, label, keystroke, Icon) {
|
360
|
+
const editor = this.editor;
|
361
|
+
editor.ui.componentFactory.add(name, ()=>{
|
362
|
+
const buttonView = this._createButton(ButtonView, name, label, keystroke, Icon);
|
363
|
+
buttonView.set({
|
364
|
+
tooltip: true
|
365
|
+
});
|
366
|
+
return buttonView;
|
367
|
+
});
|
368
|
+
editor.ui.componentFactory.add('menuBar:' + name, ()=>{
|
369
|
+
return this._createButton(MenuBarMenuListItemButtonView, name, label, keystroke, Icon);
|
370
|
+
});
|
371
|
+
}
|
372
|
+
/**
|
373
|
+
* TODO
|
374
|
+
*/ _createButton(ButtonClass, name, label, keystroke, Icon) {
|
375
|
+
const editor = this.editor;
|
376
|
+
const locale = editor.locale;
|
377
|
+
const command = editor.commands.get(name);
|
378
|
+
const view = new ButtonClass(locale);
|
379
|
+
view.set({
|
380
|
+
label,
|
381
|
+
icon: Icon,
|
382
|
+
keystroke
|
383
|
+
});
|
384
|
+
view.bind('isEnabled').to(command, 'isEnabled');
|
385
|
+
this.listenTo(view, 'execute', ()=>{
|
386
|
+
editor.execute(name);
|
387
|
+
editor.editing.view.focus();
|
388
|
+
});
|
389
|
+
return view;
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
class Undo extends Plugin {
|
394
|
+
/**
|
395
|
+
* @inheritDoc
|
396
|
+
*/ static get requires() {
|
397
|
+
return [
|
398
|
+
UndoEditing,
|
399
|
+
UndoUI
|
400
|
+
];
|
401
|
+
}
|
402
|
+
/**
|
403
|
+
* @inheritDoc
|
404
|
+
*/ static get pluginName() {
|
405
|
+
return 'Undo';
|
406
|
+
}
|
407
|
+
}
|
408
|
+
|
409
|
+
export { Undo, UndoEditing, UndoUI };
|
410
|
+
//# sourceMappingURL=index.js.map
|