@ckeditor/ckeditor5-undo 35.2.1 → 35.3.1

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-undo",
3
- "version": "35.2.1",
3
+ "version": "35.3.1",
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.2.1",
16
- "@ckeditor/ckeditor5-engine": "^35.2.1",
17
- "@ckeditor/ckeditor5-ui": "^35.2.1"
15
+ "@ckeditor/ckeditor5-core": "^35.3.1",
16
+ "@ckeditor/ckeditor5-engine": "^35.3.1",
17
+ "@ckeditor/ckeditor5-ui": "^35.3.1"
18
18
  },
19
19
  "devDependencies": {
20
- "@ckeditor/ckeditor5-basic-styles": "^35.2.1",
21
- "@ckeditor/ckeditor5-clipboard": "^35.2.1",
22
- "@ckeditor/ckeditor5-editor-classic": "^35.2.1",
23
- "@ckeditor/ckeditor5-enter": "^35.2.1",
24
- "@ckeditor/ckeditor5-heading": "^35.2.1",
25
- "@ckeditor/ckeditor5-paragraph": "^35.2.1",
26
- "@ckeditor/ckeditor5-typing": "^35.2.1",
27
- "@ckeditor/ckeditor5-table": "^35.2.1",
28
- "@ckeditor/ckeditor5-utils": "^35.2.1"
20
+ "@ckeditor/ckeditor5-basic-styles": "^35.3.1",
21
+ "@ckeditor/ckeditor5-clipboard": "^35.3.1",
22
+ "@ckeditor/ckeditor5-editor-classic": "^35.3.1",
23
+ "@ckeditor/ckeditor5-enter": "^35.3.1",
24
+ "@ckeditor/ckeditor5-heading": "^35.3.1",
25
+ "@ckeditor/ckeditor5-paragraph": "^35.3.1",
26
+ "@ckeditor/ckeditor5-typing": "^35.3.1",
27
+ "@ckeditor/ckeditor5-table": "^35.3.1",
28
+ "@ckeditor/ckeditor5-utils": "^35.3.1",
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
  }
@@ -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
- constructor( editor ) {
21
- super( editor );
22
-
23
- /**
24
- * Stack of items stored by the command. These are pairs of:
25
- *
26
- * * {@link module:engine/model/batch~Batch batch} saved by the command,
27
- * * {@link module:engine/model/selection~Selection selection} state at the moment of saving the batch.
28
- *
29
- * @protected
30
- * @member {Array} #_stack
31
- */
32
- this._stack = [];
33
-
34
- /**
35
- * Stores all batches that were created by this command.
36
- *
37
- * @protected
38
- * @member {WeakSet.<module:engine/model/batch~Batch>} #_createdBatches
39
- */
40
- this._createdBatches = new WeakSet();
41
-
42
- // Refresh state, so the command is inactive right after initialization.
43
- this.refresh();
44
-
45
- // Set the transparent batch for the `editor.data.set()` call if the
46
- // batch type is not set already.
47
- this.listenTo( editor.data, 'set', ( evt, data ) => {
48
- // Create a shallow copy of the options to not change the original args.
49
- // And make sure that an object is assigned to data[ 1 ].
50
- data[ 1 ] = { ...data[ 1 ] };
51
-
52
- const options = data[ 1 ];
53
-
54
- // If batch type is not set, default to non-undoable batch.
55
- if ( !options.batchType ) {
56
- options.batchType = { isUndoable: false };
57
- }
58
- }, { priority: 'high' } );
59
-
60
- // Clear the stack for the `transparent` batches.
61
- this.listenTo( editor.data, 'set', ( evt, data ) => {
62
- // We can assume that the object exists and it has a `batchType` property.
63
- // It was ensured with a higher priority listener before.
64
- const options = data[ 1 ];
65
-
66
- if ( !options.batchType.isUndoable ) {
67
- this.clearStack();
68
- }
69
- } );
70
- }
71
-
72
- /**
73
- * @inheritDoc
74
- */
75
- refresh() {
76
- this.isEnabled = this._stack.length > 0;
77
- }
78
-
79
- /**
80
- * Stores a batch in the command, together with the selection state of the {@link module:engine/model/document~Document document}
81
- * created by the editor which this command is registered to.
82
- *
83
- * @param {module:engine/model/batch~Batch} batch The batch to add.
84
- */
85
- addBatch( batch ) {
86
- const docSelection = this.editor.model.document.selection;
87
-
88
- const selection = {
89
- ranges: docSelection.hasOwnRange ? Array.from( docSelection.getRanges() ) : [],
90
- isBackward: docSelection.isBackward
91
- };
92
-
93
- this._stack.push( { batch, selection } );
94
- this.refresh();
95
- }
96
-
97
- /**
98
- * Removes all items from the stack.
99
- */
100
- clearStack() {
101
- this._stack = [];
102
- this.refresh();
103
- }
104
-
105
- /**
106
- * Restores the {@link module:engine/model/document~Document#selection document selection} state after a batch was undone.
107
- *
108
- * @protected
109
- * @param {Array.<module:engine/model/range~Range>} ranges Ranges to be restored.
110
- * @param {Boolean} isBackward A flag describing whether the restored range was selected forward or backward.
111
- * @param {Array.<module:engine/model/operation/operation~Operation>} operations Operations which has been applied
112
- * since selection has been stored.
113
- */
114
- _restoreSelection( ranges, isBackward, operations ) {
115
- const model = this.editor.model;
116
- const document = model.document;
117
-
118
- // This will keep the transformed selection ranges.
119
- const selectionRanges = [];
120
-
121
- // Transform all ranges from the restored selection.
122
- const transformedRangeGroups = ranges.map( range => range.getTransformedByOperations( operations ) );
123
- const allRanges = transformedRangeGroups.flat();
124
-
125
- for ( const rangeGroup of transformedRangeGroups ) {
126
- // While transforming there could appear ranges that are contained by other ranges, we shall ignore them.
127
- const transformed = rangeGroup
128
- .filter( range => range.root != document.graveyard )
129
- .filter( range => !isRangeContainedByAnyOtherRange( range, allRanges ) );
130
-
131
- // All the transformed ranges ended up in graveyard.
132
- if ( !transformed.length ) {
133
- continue;
134
- }
135
-
136
- // After the range got transformed, we have an array of ranges. Some of those
137
- // ranges may be "touching" -- they can be next to each other and could be merged.
138
- normalizeRanges( transformed );
139
-
140
- // For each `range` from `ranges`, we take only one transformed range.
141
- // This is because we want to prevent situation where single-range selection
142
- // got transformed to multi-range selection.
143
- selectionRanges.push( transformed[ 0 ] );
144
- }
145
-
146
- // @if CK_DEBUG_ENGINE // console.log( `Restored selection by undo: ${ selectionRanges.join( ', ' ) }` );
147
-
148
- // `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
149
- if ( selectionRanges.length ) {
150
- model.change( writer => {
151
- writer.setSelection( selectionRanges, { backward: isBackward } );
152
- } );
153
- }
154
- }
155
-
156
- /**
157
- * Undoes a batch by reversing that batch, transforming reversed batch and finally applying it.
158
- * This is a helper method for {@link #execute}.
159
- *
160
- * @protected
161
- * @param {module:engine/model/batch~Batch} batchToUndo The batch to be undone.
162
- * @param {module:engine/model/batch~Batch} undoingBatch The batch that will contain undoing changes.
163
- */
164
- _undo( batchToUndo, undoingBatch ) {
165
- const model = this.editor.model;
166
- const document = model.document;
167
-
168
- // All changes done by the command execution will be saved as one batch.
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( ranges ) {
210
- ranges.sort( ( a, b ) => a.start.isBefore( b.start ) ? -1 : 1 );
211
-
212
- for ( let i = 1; i < ranges.length; i++ ) {
213
- const previousRange = ranges[ i - 1 ];
214
- const joinedRange = previousRange.getJoined( ranges[ i ], true );
215
-
216
- if ( joinedRange ) {
217
- // Replace the ranges on the list with the new joined range.
218
- i--;
219
- ranges.splice( i, 2, joinedRange );
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
- function isRangeContainedByAnyOtherRange( range, ranges ) {
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';
@@ -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
- * Executes the command. This method reverts the last {@link module:engine/model/batch~Batch batch} added to
25
- * the command's stack, applies the reverted and transformed version on the
26
- * {@link module:engine/model/document~Document document} and removes the batch from the stack.
27
- * Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
28
- *
29
- * @fires execute
30
- */
31
- execute() {
32
- const item = this._stack.pop();
33
- const redoingBatch = this.editor.model.createBatch( { isUndo: true } );
34
-
35
- // All changes have to be done in one `enqueueChange` callback so other listeners will not step between consecutive
36
- // operations, or won't do changes to the document before selection is properly restored.
37
- this.editor.model.enqueueChange( redoingBatch, () => {
38
- const lastOperation = item.batch.operations[ item.batch.operations.length - 1 ];
39
- const nextBaseVersion = lastOperation.baseVersion + 1;
40
- const operations = this.editor.model.document.history.getOperations( nextBaseVersion );
41
-
42
- this._restoreSelection( item.selection.ranges, item.selection.isBackward, operations );
43
- this._undo( item.batch, redoingBatch );
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
- * @inheritDoc
108
- */
109
- static get requires() {
110
- return [ UndoEditing, UndoUI ];
111
- }
112
-
113
- /**
114
- * @inheritDoc
115
- */
116
- static get pluginName() {
117
- return 'Undo';
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
  }
@@ -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
- * Executes the command. This method reverts a {@link module:engine/model/batch~Batch batch} added to the command's stack, transforms
24
- * and applies the reverted version on the {@link module:engine/model/document~Document document} and removes the batch from the stack.
25
- * Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
26
- *
27
- * @fires execute
28
- * @fires revert
29
- * @param {module:engine/model/batch~Batch} [batch] A batch that should be undone. If not set, the last added batch will be undone.
30
- */
31
- execute( batch = null ) {
32
- // If batch is not given, set `batchIndex` to the last index in command stack.
33
- const batchIndex = batch ? this._stack.findIndex( a => a.batch == batch ) : this._stack.length - 1;
34
-
35
- const item = this._stack.splice( batchIndex, 1 )[ 0 ];
36
- const undoingBatch = this.editor.model.createBatch( { isUndo: true } );
37
-
38
- // All changes has to be done in one `enqueueChange` callback so other listeners will not
39
- // step between consecutive operations, or won't do changes to the document before selection is properly restored.
40
- this.editor.model.enqueueChange( undoingBatch, () => {
41
- this._undo( item.batch, undoingBatch );
42
-
43
- const operations = this.editor.model.document.history.getOperations( item.batch.baseVersion );
44
- this._restoreSelection( item.selection.ranges, item.selection.isBackward, operations );
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
- */
@@ -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
- * @inheritDoc
24
- */
25
- static get pluginName() {
26
- return 'UndoEditing';
27
- }
28
-
29
- /**
30
- * @inheritDoc
31
- */
32
- constructor( editor ) {
33
- super( editor );
34
-
35
- /**
36
- * The command that manages undo {@link module:engine/model/batch~Batch batches} stack (history).
37
- * Created and registered during the {@link #init feature initialization}.
38
- *
39
- * @private
40
- * @member {module:undo/undocommand~UndoCommand} #_undoCommand
41
- */
42
-
43
- /**
44
- * The command that manages redo {@link module:engine/model/batch~Batch batches} stack (history).
45
- * Created and registered during the {@link #init feature initialization}.
46
- *
47
- * @private
48
- * @member {module:undo/undocommand~UndoCommand} #_redoCommand
49
- */
50
-
51
- /**
52
- * Keeps track of which batches were registered in undo.
53
- *
54
- * @private
55
- * @member {WeakSet.<module:engine/model/batch~Batch>}
56
- */
57
- this._batchRegistry = new WeakSet();
58
- }
59
-
60
- /**
61
- * @inheritDoc
62
- */
63
- init() {
64
- const editor = this.editor;
65
-
66
- // Create commands.
67
- this._undoCommand = new UndoCommand( editor );
68
- this._redoCommand = new RedoCommand( editor );
69
-
70
- // Register command to the editor.
71
- editor.commands.add( 'undo', this._undoCommand );
72
- editor.commands.add( 'redo', this._redoCommand );
73
-
74
- this.listenTo( editor.model, 'applyOperation', ( evt, args ) => {
75
- const operation = args[ 0 ];
76
-
77
- // Do not register batch if the operation is not a document operation.
78
- // This prevents from creating empty undo steps, where all operations where non-document operations.
79
- // Non-document operations creates and alters content in detached tree fragments (for example, document fragments).
80
- // Most of time this is preparing data before it is inserted into actual tree (for example during copy & paste).
81
- // Such operations should not be reversed.
82
- if ( !operation.isDocumentOperation ) {
83
- return;
84
- }
85
-
86
- const batch = operation.batch;
87
-
88
- const isRedoBatch = this._redoCommand._createdBatches.has( batch );
89
- const isUndoBatch = this._undoCommand._createdBatches.has( batch );
90
- const wasProcessed = this._batchRegistry.has( batch );
91
-
92
- // Skip the batch if it was already processed.
93
- if ( wasProcessed ) {
94
- return;
95
- }
96
-
97
- // Add the batch to the registry so it will not be processed again.
98
- this._batchRegistry.add( batch );
99
-
100
- if ( !batch.isUndoable ) {
101
- return;
102
- }
103
-
104
- if ( isRedoBatch ) {
105
- // If this batch comes from `redoCommand`, add it to the `undoCommand` stack.
106
- this._undoCommand.addBatch( batch );
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
- * @inheritDoc
24
- */
25
- static get pluginName() {
26
- return 'UndoUI';
27
- }
28
-
29
- /**
30
- * @inheritDoc
31
- */
32
- init() {
33
- const editor = this.editor;
34
- const locale = editor.locale;
35
- const t = editor.t;
36
-
37
- const localizedUndoIcon = locale.uiLanguageDirection == 'ltr' ? undoIcon : redoIcon;
38
- const localizedRedoIcon = locale.uiLanguageDirection == 'ltr' ? redoIcon : undoIcon;
39
-
40
- this._addButton( 'undo', t( 'Undo' ), 'CTRL+Z', localizedUndoIcon );
41
- this._addButton( 'redo', t( 'Redo' ), 'CTRL+Y', localizedRedoIcon );
42
- }
43
-
44
- /**
45
- * Creates a button for the specified command.
46
- *
47
- * @private
48
- * @param {String} name Command name.
49
- * @param {String} label Button label.
50
- * @param {String} keystroke Command keystroke.
51
- * @param {String} Icon Source of the icon.
52
- */
53
- _addButton( name, label, keystroke, Icon ) {
54
- const editor = this.editor;
55
-
56
- editor.ui.componentFactory.add( name, locale => {
57
- const command = editor.commands.get( name );
58
- const view = new ButtonView( locale );
59
-
60
- view.set( {
61
- label,
62
- icon: Icon,
63
- keystroke,
64
- tooltip: true
65
- } );
66
-
67
- view.bind( 'isEnabled' ).to( command, 'isEnabled' );
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
  }