@ckeditor/ckeditor5-undo 35.2.0 → 35.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-undo",
3
- "version": "35.2.0",
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.2.0",
16
- "@ckeditor/ckeditor5-engine": "^35.2.0",
17
- "@ckeditor/ckeditor5-ui": "^35.2.0"
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.2.0",
21
- "@ckeditor/ckeditor5-clipboard": "^35.2.0",
22
- "@ckeditor/ckeditor5-editor-classic": "^35.2.0",
23
- "@ckeditor/ckeditor5-enter": "^35.2.0",
24
- "@ckeditor/ckeditor5-heading": "^35.2.0",
25
- "@ckeditor/ckeditor5-paragraph": "^35.2.0",
26
- "@ckeditor/ckeditor5-typing": "^35.2.0",
27
- "@ckeditor/ckeditor5-table": "^35.2.0",
28
- "@ckeditor/ckeditor5-utils": "^35.2.0"
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
  }
@@ -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
  }