@ckeditor/ckeditor5-typing 41.4.1 → 42.0.0-alpha.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/README.md +6 -0
- package/dist/index.js +498 -285
- package/dist/index.js.map +1 -1
- package/dist/types/textwatcher.d.ts +1 -1
- package/package.json +4 -4
- package/src/textwatcher.d.ts +1 -1
- package/src/textwatcher.js +1 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { escapeRegExp } from 'lodash-es';
|
|
|
11
11
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
12
12
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
13
13
|
*/ /**
|
|
14
|
+
* @module typing/utils/changebuffer
|
|
15
|
+
*/ /**
|
|
14
16
|
* Change buffer allows to group atomic changes (like characters that have been typed) into
|
|
15
17
|
* {@link module:engine/model/batch~Batch batches}.
|
|
16
18
|
*
|
|
@@ -30,9 +32,58 @@ import { escapeRegExp } from 'lodash-es';
|
|
|
30
32
|
* ```
|
|
31
33
|
*/ class ChangeBuffer {
|
|
32
34
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
* The model instance.
|
|
36
|
+
*/ model;
|
|
37
|
+
/**
|
|
38
|
+
* The maximum number of atomic changes which can be contained in one batch.
|
|
39
|
+
*/ limit;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the buffer is locked. A locked buffer cannot be reset unless it gets unlocked.
|
|
42
|
+
*/ _isLocked;
|
|
43
|
+
/**
|
|
44
|
+
* The number of atomic changes in the buffer. Once it exceeds the {@link #limit},
|
|
45
|
+
* the {@link #batch batch} is set to a new one.
|
|
46
|
+
*/ _size;
|
|
47
|
+
/**
|
|
48
|
+
* The current batch instance.
|
|
49
|
+
*/ _batch = null;
|
|
50
|
+
/**
|
|
51
|
+
* The callback to document the change event which later needs to be removed.
|
|
52
|
+
*/ _changeCallback;
|
|
53
|
+
/**
|
|
54
|
+
* The callback to document selection `change:attribute` and `change:range` events which resets the buffer.
|
|
55
|
+
*/ _selectionChangeCallback;
|
|
56
|
+
/**
|
|
57
|
+
* Creates a new instance of the change buffer.
|
|
58
|
+
*
|
|
59
|
+
* @param limit The maximum number of atomic changes which can be contained in one batch.
|
|
60
|
+
*/ constructor(model, limit = 20){
|
|
61
|
+
this.model = model;
|
|
62
|
+
this._size = 0;
|
|
63
|
+
this.limit = limit;
|
|
64
|
+
this._isLocked = false;
|
|
65
|
+
// The function to be called in order to notify the buffer about batches which appeared in the document.
|
|
66
|
+
// The callback will check whether it is a new batch and in that case the buffer will be flushed.
|
|
67
|
+
//
|
|
68
|
+
// The reason why the buffer needs to be flushed whenever a new batch appears is that the changes added afterwards
|
|
69
|
+
// should be added to a new batch. For instance, when the user types, then inserts an image, and then types again,
|
|
70
|
+
// the characters typed after inserting the image should be added to a different batch than the characters typed before.
|
|
71
|
+
this._changeCallback = (evt, batch)=>{
|
|
72
|
+
if (batch.isLocal && batch.isUndoable && batch !== this._batch) {
|
|
73
|
+
this._reset(true);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
this._selectionChangeCallback = ()=>{
|
|
77
|
+
this._reset();
|
|
78
|
+
};
|
|
79
|
+
this.model.document.on('change', this._changeCallback);
|
|
80
|
+
this.model.document.selection.on('change:range', this._selectionChangeCallback);
|
|
81
|
+
this.model.document.selection.on('change:attribute', this._selectionChangeCallback);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The current batch to which a feature should add its operations. Once the {@link #size}
|
|
85
|
+
* is reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.
|
|
86
|
+
*/ get batch() {
|
|
36
87
|
if (!this._batch) {
|
|
37
88
|
this._batch = this.model.createBatch({
|
|
38
89
|
isTyping: true
|
|
@@ -41,106 +92,92 @@ import { escapeRegExp } from 'lodash-es';
|
|
|
41
92
|
return this._batch;
|
|
42
93
|
}
|
|
43
94
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
95
|
+
* The number of atomic changes in the buffer. Once it exceeds the {@link #limit},
|
|
96
|
+
* the {@link #batch batch} is set to a new one.
|
|
97
|
+
*/ get size() {
|
|
47
98
|
return this._size;
|
|
48
99
|
}
|
|
49
100
|
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
101
|
+
* The input number of changes into the buffer. Once the {@link #size} is
|
|
102
|
+
* reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.
|
|
103
|
+
*
|
|
104
|
+
* @param changeCount The number of atomic changes to input.
|
|
105
|
+
*/ input(changeCount) {
|
|
55
106
|
this._size += changeCount;
|
|
56
107
|
if (this._size >= this.limit) {
|
|
57
108
|
this._reset(true);
|
|
58
109
|
}
|
|
59
110
|
}
|
|
60
111
|
/**
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
* Whether the buffer is locked. A locked buffer cannot be reset unless it gets unlocked.
|
|
113
|
+
*/ get isLocked() {
|
|
63
114
|
return this._isLocked;
|
|
64
115
|
}
|
|
65
116
|
/**
|
|
66
|
-
|
|
67
|
-
|
|
117
|
+
* Locks the buffer.
|
|
118
|
+
*/ lock() {
|
|
68
119
|
this._isLocked = true;
|
|
69
120
|
}
|
|
70
121
|
/**
|
|
71
|
-
|
|
72
|
-
|
|
122
|
+
* Unlocks the buffer.
|
|
123
|
+
*/ unlock() {
|
|
73
124
|
this._isLocked = false;
|
|
74
125
|
}
|
|
75
126
|
/**
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
* Destroys the buffer.
|
|
128
|
+
*/ destroy() {
|
|
78
129
|
this.model.document.off('change', this._changeCallback);
|
|
79
130
|
this.model.document.selection.off('change:range', this._selectionChangeCallback);
|
|
80
131
|
this.model.document.selection.off('change:attribute', this._selectionChangeCallback);
|
|
81
132
|
}
|
|
82
133
|
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
134
|
+
* Resets the change buffer.
|
|
135
|
+
*
|
|
136
|
+
* @param ignoreLock Whether internal lock {@link #isLocked} should be ignored.
|
|
137
|
+
*/ _reset(ignoreLock = false) {
|
|
87
138
|
if (!this.isLocked || ignoreLock) {
|
|
88
139
|
this._batch = null;
|
|
89
140
|
this._size = 0;
|
|
90
141
|
}
|
|
91
142
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Creates a new instance of the change buffer.
|
|
94
|
-
*
|
|
95
|
-
* @param limit The maximum number of atomic changes which can be contained in one batch.
|
|
96
|
-
*/ constructor(model, limit = 20){
|
|
97
|
-
/**
|
|
98
|
-
* The current batch instance.
|
|
99
|
-
*/ this._batch = null;
|
|
100
|
-
this.model = model;
|
|
101
|
-
this._size = 0;
|
|
102
|
-
this.limit = limit;
|
|
103
|
-
this._isLocked = false;
|
|
104
|
-
// The function to be called in order to notify the buffer about batches which appeared in the document.
|
|
105
|
-
// The callback will check whether it is a new batch and in that case the buffer will be flushed.
|
|
106
|
-
//
|
|
107
|
-
// The reason why the buffer needs to be flushed whenever a new batch appears is that the changes added afterwards
|
|
108
|
-
// should be added to a new batch. For instance, when the user types, then inserts an image, and then types again,
|
|
109
|
-
// the characters typed after inserting the image should be added to a different batch than the characters typed before.
|
|
110
|
-
this._changeCallback = (evt, batch)=>{
|
|
111
|
-
if (batch.isLocal && batch.isUndoable && batch !== this._batch) {
|
|
112
|
-
this._reset(true);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
this._selectionChangeCallback = ()=>{
|
|
116
|
-
this._reset();
|
|
117
|
-
};
|
|
118
|
-
this.model.document.on('change', this._changeCallback);
|
|
119
|
-
this.model.document.selection.on('change:range', this._selectionChangeCallback);
|
|
120
|
-
this.model.document.selection.on('change:attribute', this._selectionChangeCallback);
|
|
121
|
-
}
|
|
122
143
|
}
|
|
123
144
|
|
|
124
|
-
|
|
145
|
+
/**
|
|
146
|
+
* The insert text command. Used by the {@link module:typing/input~Input input feature} to handle typing.
|
|
147
|
+
*/ class InsertTextCommand extends Command {
|
|
148
|
+
/**
|
|
149
|
+
* Typing's change buffer used to group subsequent changes into batches.
|
|
150
|
+
*/ _buffer;
|
|
151
|
+
/**
|
|
152
|
+
* Creates an instance of the command.
|
|
153
|
+
*
|
|
154
|
+
* @param undoStepSize The maximum number of atomic changes
|
|
155
|
+
* which can be contained in one batch in the command buffer.
|
|
156
|
+
*/ constructor(editor, undoStepSize){
|
|
157
|
+
super(editor);
|
|
158
|
+
this._buffer = new ChangeBuffer(editor.model, undoStepSize);
|
|
159
|
+
// Since this command may execute on different selectable than selection, it should be checked directly in execute block.
|
|
160
|
+
this._isEnabledBasedOnSelection = false;
|
|
161
|
+
}
|
|
125
162
|
/**
|
|
126
|
-
|
|
127
|
-
|
|
163
|
+
* The current change buffer.
|
|
164
|
+
*/ get buffer() {
|
|
128
165
|
return this._buffer;
|
|
129
166
|
}
|
|
130
167
|
/**
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
* @inheritDoc
|
|
169
|
+
*/ destroy() {
|
|
133
170
|
super.destroy();
|
|
134
171
|
this._buffer.destroy();
|
|
135
172
|
}
|
|
136
173
|
/**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
174
|
+
* Executes the input command. It replaces the content within the given range with the given text.
|
|
175
|
+
* Replacing is a two step process, first the content within the range is removed and then the new text is inserted
|
|
176
|
+
* at the beginning of the range (which after the removal is a collapsed range).
|
|
177
|
+
*
|
|
178
|
+
* @fires execute
|
|
179
|
+
* @param options The command options.
|
|
180
|
+
*/ execute(options = {}) {
|
|
144
181
|
const model = this.editor.model;
|
|
145
182
|
const doc = model.document;
|
|
146
183
|
const text = options.text || '';
|
|
@@ -174,17 +211,6 @@ class InsertTextCommand extends Command {
|
|
|
174
211
|
this._buffer.input(textInsertions);
|
|
175
212
|
});
|
|
176
213
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Creates an instance of the command.
|
|
179
|
-
*
|
|
180
|
-
* @param undoStepSize The maximum number of atomic changes
|
|
181
|
-
* which can be contained in one batch in the command buffer.
|
|
182
|
-
*/ constructor(editor, undoStepSize){
|
|
183
|
-
super(editor);
|
|
184
|
-
this._buffer = new ChangeBuffer(editor.model, undoStepSize);
|
|
185
|
-
// Since this command may execute on different selectable than selection, it should be checked directly in execute block.
|
|
186
|
-
this._isEnabledBasedOnSelection = false;
|
|
187
|
-
}
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
const TYPING_INPUT_TYPES = [
|
|
@@ -199,16 +225,16 @@ const TYPING_INPUT_TYPES = [
|
|
|
199
225
|
// This one is used by Safari when accepting spell check suggestions from the autocorrection pop-up (Mac).
|
|
200
226
|
'insertReplacementText'
|
|
201
227
|
];
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*/ observe() {}
|
|
228
|
+
/**
|
|
229
|
+
* Text insertion observer introduces the {@link module:engine/view/document~Document#event:insertText} event.
|
|
230
|
+
*/ class InsertTextObserver extends Observer {
|
|
206
231
|
/**
|
|
207
|
-
|
|
208
|
-
|
|
232
|
+
* Instance of the focus observer. Insert text observer calls
|
|
233
|
+
* {@link module:engine/view/observer/focusobserver~FocusObserver#flush} to mark the latest focus change as complete.
|
|
234
|
+
*/ focusObserver;
|
|
209
235
|
/**
|
|
210
|
-
|
|
211
|
-
|
|
236
|
+
* @inheritDoc
|
|
237
|
+
*/ constructor(view){
|
|
212
238
|
super(view);
|
|
213
239
|
this.focusObserver = view.getObserver(FocusObserver);
|
|
214
240
|
// On Android composition events should immediately be applied to the model. Rendering is not disabled.
|
|
@@ -281,17 +307,25 @@ class InsertTextObserver extends Observer {
|
|
|
281
307
|
priority: 'lowest'
|
|
282
308
|
});
|
|
283
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* @inheritDoc
|
|
312
|
+
*/ observe() {}
|
|
313
|
+
/**
|
|
314
|
+
* @inheritDoc
|
|
315
|
+
*/ stopObserving() {}
|
|
284
316
|
}
|
|
285
317
|
|
|
286
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Handles text input coming from the keyboard or other input methods.
|
|
320
|
+
*/ class Input extends Plugin {
|
|
287
321
|
/**
|
|
288
|
-
|
|
289
|
-
|
|
322
|
+
* @inheritDoc
|
|
323
|
+
*/ static get pluginName() {
|
|
290
324
|
return 'Input';
|
|
291
325
|
}
|
|
292
326
|
/**
|
|
293
|
-
|
|
294
|
-
|
|
327
|
+
* @inheritDoc
|
|
328
|
+
*/ init() {
|
|
295
329
|
const editor = this.editor;
|
|
296
330
|
const model = editor.model;
|
|
297
331
|
const view = editor.editing.view;
|
|
@@ -407,23 +441,45 @@ function deleteSelectionContent(model, insertTextCommand) {
|
|
|
407
441
|
buffer.unlock();
|
|
408
442
|
}
|
|
409
443
|
|
|
410
|
-
|
|
444
|
+
/**
|
|
445
|
+
* The delete command. Used by the {@link module:typing/delete~Delete delete feature} to handle the <kbd>Delete</kbd> and
|
|
446
|
+
* <kbd>Backspace</kbd> keys.
|
|
447
|
+
*/ class DeleteCommand extends Command {
|
|
411
448
|
/**
|
|
412
|
-
|
|
413
|
-
|
|
449
|
+
* The directionality of the delete describing in what direction it should
|
|
450
|
+
* consume the content when the selection is collapsed.
|
|
451
|
+
*/ direction;
|
|
452
|
+
/**
|
|
453
|
+
* Delete's change buffer used to group subsequent changes into batches.
|
|
454
|
+
*/ _buffer;
|
|
455
|
+
/**
|
|
456
|
+
* Creates an instance of the command.
|
|
457
|
+
*
|
|
458
|
+
* @param direction The directionality of the delete describing in what direction it
|
|
459
|
+
* should consume the content when the selection is collapsed.
|
|
460
|
+
*/ constructor(editor, direction){
|
|
461
|
+
super(editor);
|
|
462
|
+
this.direction = direction;
|
|
463
|
+
this._buffer = new ChangeBuffer(editor.model, editor.config.get('typing.undoStep'));
|
|
464
|
+
// Since this command may execute on different selectable than selection, it should be checked directly in execute block.
|
|
465
|
+
this._isEnabledBasedOnSelection = false;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* The current change buffer.
|
|
469
|
+
*/ get buffer() {
|
|
414
470
|
return this._buffer;
|
|
415
471
|
}
|
|
416
472
|
/**
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
473
|
+
* Executes the delete command. Depending on whether the selection is collapsed or not, deletes its content
|
|
474
|
+
* or a piece of content in the {@link #direction defined direction}.
|
|
475
|
+
*
|
|
476
|
+
* @fires execute
|
|
477
|
+
* @param options The command options.
|
|
478
|
+
* @param options.unit See {@link module:engine/model/utils/modifyselection~modifySelection}'s options.
|
|
479
|
+
* @param options.sequence A number describing which subsequent delete event it is without the key being released.
|
|
480
|
+
* See the {@link module:engine/view/document~Document#event:delete} event data.
|
|
481
|
+
* @param options.selection Selection to remove. If not set, current model selection will be used.
|
|
482
|
+
*/ execute(options = {}) {
|
|
427
483
|
const model = this.editor.model;
|
|
428
484
|
const doc = model.document;
|
|
429
485
|
model.enqueueChange(this._buffer.batch, (writer)=>{
|
|
@@ -489,21 +545,21 @@ class DeleteCommand extends Command {
|
|
|
489
545
|
});
|
|
490
546
|
}
|
|
491
547
|
/**
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
548
|
+
* If the user keeps <kbd>Backspace</kbd> or <kbd>Delete</kbd> key pressed, the content of the current
|
|
549
|
+
* editable will be cleared. However, this will not yet lead to resetting the remaining block to a paragraph
|
|
550
|
+
* (which happens e.g. when the user does <kbd>Ctrl</kbd> + <kbd>A</kbd>, <kbd>Backspace</kbd>).
|
|
551
|
+
*
|
|
552
|
+
* But, if the user pressed the key in an empty editable for the first time,
|
|
553
|
+
* we want to replace the entire content with a paragraph if:
|
|
554
|
+
*
|
|
555
|
+
* * the current limit element is empty,
|
|
556
|
+
* * the paragraph is allowed in the limit element,
|
|
557
|
+
* * the limit doesn't already have a paragraph inside.
|
|
558
|
+
*
|
|
559
|
+
* See https://github.com/ckeditor/ckeditor5-typing/issues/61.
|
|
560
|
+
*
|
|
561
|
+
* @param sequence A number describing which subsequent delete event it is without the key being released.
|
|
562
|
+
*/ _shouldEntireContentBeReplacedWithParagraph(sequence) {
|
|
507
563
|
// Does nothing if user pressed and held the "Backspace" or "Delete" key.
|
|
508
564
|
if (sequence > 1) {
|
|
509
565
|
return false;
|
|
@@ -531,10 +587,10 @@ class DeleteCommand extends Command {
|
|
|
531
587
|
return true;
|
|
532
588
|
}
|
|
533
589
|
/**
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
590
|
+
* The entire content is replaced with the paragraph. Selection is moved inside the paragraph.
|
|
591
|
+
*
|
|
592
|
+
* @param writer The model writer.
|
|
593
|
+
*/ _replaceEntireContentWithParagraph(writer) {
|
|
538
594
|
const model = this.editor.model;
|
|
539
595
|
const doc = model.document;
|
|
540
596
|
const selection = doc.selection;
|
|
@@ -545,12 +601,12 @@ class DeleteCommand extends Command {
|
|
|
545
601
|
writer.setSelection(paragraph, 0);
|
|
546
602
|
}
|
|
547
603
|
/**
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
604
|
+
* Checks if the selection is inside an empty element that is the first child of the limit element
|
|
605
|
+
* and should be replaced with a paragraph.
|
|
606
|
+
*
|
|
607
|
+
* @param selection The selection.
|
|
608
|
+
* @param sequence A number describing which subsequent delete event it is without the key being released.
|
|
609
|
+
*/ _shouldReplaceFirstBlockWithParagraph(selection, sequence) {
|
|
554
610
|
const model = this.editor.model;
|
|
555
611
|
// Does nothing if user pressed and held the "Backspace" key or it was a "Delete" button.
|
|
556
612
|
if (sequence > 1 || this.direction != 'backward') {
|
|
@@ -581,18 +637,6 @@ class DeleteCommand extends Command {
|
|
|
581
637
|
}
|
|
582
638
|
return true;
|
|
583
639
|
}
|
|
584
|
-
/**
|
|
585
|
-
* Creates an instance of the command.
|
|
586
|
-
*
|
|
587
|
-
* @param direction The directionality of the delete describing in what direction it
|
|
588
|
-
* should consume the content when the selection is collapsed.
|
|
589
|
-
*/ constructor(editor, direction){
|
|
590
|
-
super(editor);
|
|
591
|
-
this.direction = direction;
|
|
592
|
-
this._buffer = new ChangeBuffer(editor.model, editor.config.get('typing.undoStep'));
|
|
593
|
-
// Since this command may execute on different selectable than selection, it should be checked directly in execute block.
|
|
594
|
-
this._isEnabledBasedOnSelection = false;
|
|
595
|
-
}
|
|
596
640
|
}
|
|
597
641
|
|
|
598
642
|
const DELETE_CHARACTER = 'character';
|
|
@@ -680,16 +724,12 @@ const DELETE_EVENT_TYPES = {
|
|
|
680
724
|
direction: DELETE_FORWARD
|
|
681
725
|
}
|
|
682
726
|
};
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
*/ observe() {}
|
|
687
|
-
/**
|
|
688
|
-
* @inheritDoc
|
|
689
|
-
*/ stopObserving() {}
|
|
727
|
+
/**
|
|
728
|
+
* Delete observer introduces the {@link module:engine/view/document~Document#event:delete} event.
|
|
729
|
+
*/ class DeleteObserver extends Observer {
|
|
690
730
|
/**
|
|
691
|
-
|
|
692
|
-
|
|
731
|
+
* @inheritDoc
|
|
732
|
+
*/ constructor(view){
|
|
693
733
|
super(view);
|
|
694
734
|
const document = view.document;
|
|
695
735
|
// It matters how many subsequent deletions were made, e.g. when the backspace key was pressed and held
|
|
@@ -748,6 +788,12 @@ class DeleteObserver extends Observer {
|
|
|
748
788
|
enableChromeWorkaround(this);
|
|
749
789
|
}
|
|
750
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* @inheritDoc
|
|
793
|
+
*/ observe() {}
|
|
794
|
+
/**
|
|
795
|
+
* @inheritDoc
|
|
796
|
+
*/ stopObserving() {}
|
|
751
797
|
}
|
|
752
798
|
/**
|
|
753
799
|
* Enables workaround for the issue https://github.com/ckeditor/ckeditor5/issues/11904.
|
|
@@ -832,15 +878,21 @@ class DeleteObserver extends Observer {
|
|
|
832
878
|
return false;
|
|
833
879
|
}
|
|
834
880
|
|
|
835
|
-
|
|
881
|
+
/**
|
|
882
|
+
* The delete and backspace feature. Handles keys such as <kbd>Delete</kbd> and <kbd>Backspace</kbd>, other
|
|
883
|
+
* keystrokes and user actions that result in deleting content in the editor.
|
|
884
|
+
*/ class Delete extends Plugin {
|
|
885
|
+
/**
|
|
886
|
+
* Whether pressing backspace should trigger undo action
|
|
887
|
+
*/ _undoOnBackspace;
|
|
836
888
|
/**
|
|
837
|
-
|
|
838
|
-
|
|
889
|
+
* @inheritDoc
|
|
890
|
+
*/ static get pluginName() {
|
|
839
891
|
return 'Delete';
|
|
840
892
|
}
|
|
841
893
|
/**
|
|
842
|
-
|
|
843
|
-
|
|
894
|
+
* @inheritDoc
|
|
895
|
+
*/ init() {
|
|
844
896
|
const editor = this.editor;
|
|
845
897
|
const view = editor.editing.view;
|
|
846
898
|
const viewDocument = view.document;
|
|
@@ -893,17 +945,22 @@ class Delete extends Plugin {
|
|
|
893
945
|
}
|
|
894
946
|
}
|
|
895
947
|
/**
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
948
|
+
* If the next user action after calling this method is pressing backspace, it would undo the last change.
|
|
949
|
+
*
|
|
950
|
+
* Requires {@link module:undo/undoediting~UndoEditing} plugin. If not loaded, does nothing.
|
|
951
|
+
*/ requestUndoOnBackspace() {
|
|
900
952
|
if (this.editor.plugins.has('UndoEditing')) {
|
|
901
953
|
this._undoOnBackspace = true;
|
|
902
954
|
}
|
|
903
955
|
}
|
|
904
956
|
}
|
|
905
957
|
|
|
906
|
-
|
|
958
|
+
/**
|
|
959
|
+
* The typing feature. It handles typing.
|
|
960
|
+
*
|
|
961
|
+
* This is a "glue" plugin which loads the {@link module:typing/input~Input} and {@link module:typing/delete~Delete}
|
|
962
|
+
* plugins.
|
|
963
|
+
*/ class Typing extends Plugin {
|
|
907
964
|
static get requires() {
|
|
908
965
|
return [
|
|
909
966
|
Input,
|
|
@@ -911,8 +968,8 @@ class Typing extends Plugin {
|
|
|
911
968
|
];
|
|
912
969
|
}
|
|
913
970
|
/**
|
|
914
|
-
|
|
915
|
-
|
|
971
|
+
* @inheritDoc
|
|
972
|
+
*/ static get pluginName() {
|
|
916
973
|
return 'Typing';
|
|
917
974
|
}
|
|
918
975
|
}
|
|
@@ -921,6 +978,8 @@ class Typing extends Plugin {
|
|
|
921
978
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
922
979
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
923
980
|
*/ /**
|
|
981
|
+
* @module typing/utils/getlasttextline
|
|
982
|
+
*/ /**
|
|
924
983
|
* Returns the last text line from the given range.
|
|
925
984
|
*
|
|
926
985
|
* "The last text line" is understood as text (from one or more text nodes) which is limited either by a parent block
|
|
@@ -964,15 +1023,57 @@ class Typing extends Plugin {
|
|
|
964
1023
|
};
|
|
965
1024
|
}
|
|
966
1025
|
|
|
967
|
-
|
|
1026
|
+
/**
|
|
1027
|
+
* The text watcher feature.
|
|
1028
|
+
*
|
|
1029
|
+
* Fires the {@link module:typing/textwatcher~TextWatcher#event:matched:data `matched:data`},
|
|
1030
|
+
* {@link module:typing/textwatcher~TextWatcher#event:matched:selection `matched:selection`} and
|
|
1031
|
+
* {@link module:typing/textwatcher~TextWatcher#event:unmatched `unmatched`} events on typing or selection changes.
|
|
1032
|
+
*/ class TextWatcher extends /* #__PURE__ */ ObservableMixin() {
|
|
1033
|
+
/**
|
|
1034
|
+
* The editor's model.
|
|
1035
|
+
*/ model;
|
|
1036
|
+
/**
|
|
1037
|
+
* The function used to match the text.
|
|
1038
|
+
*
|
|
1039
|
+
* The test callback can return 3 values:
|
|
1040
|
+
*
|
|
1041
|
+
* * `false` if there is no match,
|
|
1042
|
+
* * `true` if there is a match,
|
|
1043
|
+
* * an object if there is a match and we want to pass some additional information to the {@link #event:matched:data} event.
|
|
1044
|
+
*/ testCallback;
|
|
1045
|
+
/**
|
|
1046
|
+
* Whether there is a match currently.
|
|
1047
|
+
*/ _hasMatch;
|
|
1048
|
+
/**
|
|
1049
|
+
* Creates a text watcher instance.
|
|
1050
|
+
*
|
|
1051
|
+
* @param testCallback See {@link module:typing/textwatcher~TextWatcher#testCallback}.
|
|
1052
|
+
*/ constructor(model, testCallback){
|
|
1053
|
+
super();
|
|
1054
|
+
this.model = model;
|
|
1055
|
+
this.testCallback = testCallback;
|
|
1056
|
+
this._hasMatch = false;
|
|
1057
|
+
this.set('isEnabled', true);
|
|
1058
|
+
// Toggle text watching on isEnabled state change.
|
|
1059
|
+
this.on('change:isEnabled', ()=>{
|
|
1060
|
+
if (this.isEnabled) {
|
|
1061
|
+
this._startListening();
|
|
1062
|
+
} else {
|
|
1063
|
+
this.stopListening(model.document.selection);
|
|
1064
|
+
this.stopListening(model.document);
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
this._startListening();
|
|
1068
|
+
}
|
|
968
1069
|
/**
|
|
969
|
-
|
|
970
|
-
|
|
1070
|
+
* Flag indicating whether there is a match currently.
|
|
1071
|
+
*/ get hasMatch() {
|
|
971
1072
|
return this._hasMatch;
|
|
972
1073
|
}
|
|
973
1074
|
/**
|
|
974
|
-
|
|
975
|
-
|
|
1075
|
+
* Starts listening to the editor for typing and selection events.
|
|
1076
|
+
*/ _startListening() {
|
|
976
1077
|
const model = this.model;
|
|
977
1078
|
const document = model.document;
|
|
978
1079
|
this.listenTo(document.selection, 'change:range', (evt, { directChange })=>{
|
|
@@ -1000,15 +1101,15 @@ class TextWatcher extends ObservableMixin() {
|
|
|
1000
1101
|
});
|
|
1001
1102
|
}
|
|
1002
1103
|
/**
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1104
|
+
* Checks the editor content for matched text.
|
|
1105
|
+
*
|
|
1106
|
+
* @fires matched:data
|
|
1107
|
+
* @fires matched:selection
|
|
1108
|
+
* @fires unmatched
|
|
1109
|
+
*
|
|
1110
|
+
* @param suffix A suffix used for generating the event name.
|
|
1111
|
+
* @param data Data object for event.
|
|
1112
|
+
*/ _evaluateTextBeforeSelection(suffix, data = {}) {
|
|
1012
1113
|
const model = this.model;
|
|
1013
1114
|
const document = model.document;
|
|
1014
1115
|
const selection = document.selection;
|
|
@@ -1031,38 +1132,158 @@ class TextWatcher extends ObservableMixin() {
|
|
|
1031
1132
|
this.fire(`matched:${suffix}`, eventData);
|
|
1032
1133
|
}
|
|
1033
1134
|
}
|
|
1034
|
-
/**
|
|
1035
|
-
* Creates a text watcher instance.
|
|
1036
|
-
*
|
|
1037
|
-
* @param testCallback See {@link module:typing/textwatcher~TextWatcher#testCallback}.
|
|
1038
|
-
*/ constructor(model, testCallback){
|
|
1039
|
-
super();
|
|
1040
|
-
this.model = model;
|
|
1041
|
-
this.testCallback = testCallback;
|
|
1042
|
-
this._hasMatch = false;
|
|
1043
|
-
this.set('isEnabled', true);
|
|
1044
|
-
// Toggle text watching on isEnabled state change.
|
|
1045
|
-
this.on('change:isEnabled', ()=>{
|
|
1046
|
-
if (this.isEnabled) {
|
|
1047
|
-
this._startListening();
|
|
1048
|
-
} else {
|
|
1049
|
-
this.stopListening(model.document.selection);
|
|
1050
|
-
this.stopListening(model.document);
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
this._startListening();
|
|
1054
|
-
}
|
|
1055
1135
|
}
|
|
1056
1136
|
|
|
1057
|
-
|
|
1137
|
+
/**
|
|
1138
|
+
* This plugin enables the two-step caret (phantom) movement behavior for
|
|
1139
|
+
* {@link module:typing/twostepcaretmovement~TwoStepCaretMovement#registerAttribute registered attributes}
|
|
1140
|
+
* on arrow right (<kbd>→</kbd>) and left (<kbd>←</kbd>) key press.
|
|
1141
|
+
*
|
|
1142
|
+
* Thanks to this (phantom) caret movement the user is able to type before/after as well as at the
|
|
1143
|
+
* beginning/end of an attribute.
|
|
1144
|
+
*
|
|
1145
|
+
* **Note:** This plugin support right–to–left (Arabic, Hebrew, etc.) content by mirroring its behavior
|
|
1146
|
+
* but for the sake of simplicity examples showcase only left–to–right use–cases.
|
|
1147
|
+
*
|
|
1148
|
+
* # Forward movement
|
|
1149
|
+
*
|
|
1150
|
+
* ## "Entering" an attribute:
|
|
1151
|
+
*
|
|
1152
|
+
* When this plugin is enabled and registered for the `a` attribute and the selection is right before it
|
|
1153
|
+
* (at the attribute boundary), pressing the right arrow key will not move the selection but update its
|
|
1154
|
+
* attributes accordingly:
|
|
1155
|
+
*
|
|
1156
|
+
* * When enabled:
|
|
1157
|
+
*
|
|
1158
|
+
* ```xml
|
|
1159
|
+
* foo{}<$text a="true">bar</$text>
|
|
1160
|
+
* ```
|
|
1161
|
+
*
|
|
1162
|
+
* <kbd>→</kbd>
|
|
1163
|
+
*
|
|
1164
|
+
* ```xml
|
|
1165
|
+
* foo<$text a="true">{}bar</$text>
|
|
1166
|
+
* ```
|
|
1167
|
+
*
|
|
1168
|
+
* * When disabled:
|
|
1169
|
+
*
|
|
1170
|
+
* ```xml
|
|
1171
|
+
* foo{}<$text a="true">bar</$text>
|
|
1172
|
+
* ```
|
|
1173
|
+
*
|
|
1174
|
+
* <kbd>→</kbd>
|
|
1175
|
+
*
|
|
1176
|
+
* ```xml
|
|
1177
|
+
* foo<$text a="true">b{}ar</$text>
|
|
1178
|
+
* ```
|
|
1179
|
+
*
|
|
1180
|
+
*
|
|
1181
|
+
* ## "Leaving" an attribute:
|
|
1182
|
+
*
|
|
1183
|
+
* * When enabled:
|
|
1184
|
+
*
|
|
1185
|
+
* ```xml
|
|
1186
|
+
* <$text a="true">bar{}</$text>baz
|
|
1187
|
+
* ```
|
|
1188
|
+
*
|
|
1189
|
+
* <kbd>→</kbd>
|
|
1190
|
+
*
|
|
1191
|
+
* ```xml
|
|
1192
|
+
* <$text a="true">bar</$text>{}baz
|
|
1193
|
+
* ```
|
|
1194
|
+
*
|
|
1195
|
+
* * When disabled:
|
|
1196
|
+
*
|
|
1197
|
+
* ```xml
|
|
1198
|
+
* <$text a="true">bar{}</$text>baz
|
|
1199
|
+
* ```
|
|
1200
|
+
*
|
|
1201
|
+
* <kbd>→</kbd>
|
|
1202
|
+
*
|
|
1203
|
+
* ```xml
|
|
1204
|
+
* <$text a="true">bar</$text>b{}az
|
|
1205
|
+
* ```
|
|
1206
|
+
*
|
|
1207
|
+
* # Backward movement
|
|
1208
|
+
*
|
|
1209
|
+
* * When enabled:
|
|
1210
|
+
*
|
|
1211
|
+
* ```xml
|
|
1212
|
+
* <$text a="true">bar</$text>{}baz
|
|
1213
|
+
* ```
|
|
1214
|
+
*
|
|
1215
|
+
* <kbd>←</kbd>
|
|
1216
|
+
*
|
|
1217
|
+
* ```xml
|
|
1218
|
+
* <$text a="true">bar{}</$text>baz
|
|
1219
|
+
* ```
|
|
1220
|
+
*
|
|
1221
|
+
* * When disabled:
|
|
1222
|
+
*
|
|
1223
|
+
* ```xml
|
|
1224
|
+
* <$text a="true">bar</$text>{}baz
|
|
1225
|
+
* ```
|
|
1226
|
+
*
|
|
1227
|
+
* <kbd>←</kbd>
|
|
1228
|
+
*
|
|
1229
|
+
* ```xml
|
|
1230
|
+
* <$text a="true">ba{}r</$text>b{}az
|
|
1231
|
+
* ```
|
|
1232
|
+
*
|
|
1233
|
+
* # Multiple attributes
|
|
1234
|
+
*
|
|
1235
|
+
* * When enabled and many attributes starts or ends at the same position:
|
|
1236
|
+
*
|
|
1237
|
+
* ```xml
|
|
1238
|
+
* <$text a="true" b="true">bar</$text>{}baz
|
|
1239
|
+
* ```
|
|
1240
|
+
*
|
|
1241
|
+
* <kbd>←</kbd>
|
|
1242
|
+
*
|
|
1243
|
+
* ```xml
|
|
1244
|
+
* <$text a="true" b="true">bar{}</$text>baz
|
|
1245
|
+
* ```
|
|
1246
|
+
*
|
|
1247
|
+
* * When enabled and one procedes another:
|
|
1248
|
+
*
|
|
1249
|
+
* ```xml
|
|
1250
|
+
* <$text a="true">bar</$text><$text b="true">{}bar</$text>
|
|
1251
|
+
* ```
|
|
1252
|
+
*
|
|
1253
|
+
* <kbd>←</kbd>
|
|
1254
|
+
*
|
|
1255
|
+
* ```xml
|
|
1256
|
+
* <$text a="true">bar{}</$text><$text b="true">bar</$text>
|
|
1257
|
+
* ```
|
|
1258
|
+
*
|
|
1259
|
+
*/ class TwoStepCaretMovement extends Plugin {
|
|
1260
|
+
/**
|
|
1261
|
+
* A set of attributes to handle.
|
|
1262
|
+
*/ attributes;
|
|
1058
1263
|
/**
|
|
1059
|
-
|
|
1060
|
-
|
|
1264
|
+
* The current UID of the overridden gravity, as returned by
|
|
1265
|
+
* {@link module:engine/model/writer~Writer#overrideSelectionGravity}.
|
|
1266
|
+
*/ _overrideUid;
|
|
1267
|
+
/**
|
|
1268
|
+
* A flag indicating that the automatic gravity restoration should not happen upon the next
|
|
1269
|
+
* gravity restoration.
|
|
1270
|
+
* {@link module:engine/model/selection~Selection#event:change:range} event.
|
|
1271
|
+
*/ _isNextGravityRestorationSkipped = false;
|
|
1272
|
+
/**
|
|
1273
|
+
* @inheritDoc
|
|
1274
|
+
*/ static get pluginName() {
|
|
1061
1275
|
return 'TwoStepCaretMovement';
|
|
1062
1276
|
}
|
|
1063
1277
|
/**
|
|
1064
|
-
|
|
1065
|
-
|
|
1278
|
+
* @inheritDoc
|
|
1279
|
+
*/ constructor(editor){
|
|
1280
|
+
super(editor);
|
|
1281
|
+
this.attributes = new Set();
|
|
1282
|
+
this._overrideUid = null;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* @inheritDoc
|
|
1286
|
+
*/ init() {
|
|
1066
1287
|
const editor = this.editor;
|
|
1067
1288
|
const model = editor.model;
|
|
1068
1289
|
const view = editor.editing.view;
|
|
@@ -1131,19 +1352,19 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1131
1352
|
this._handleDeleteContentAfterNode();
|
|
1132
1353
|
}
|
|
1133
1354
|
/**
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1355
|
+
* Registers a given attribute for the two-step caret movement.
|
|
1356
|
+
*
|
|
1357
|
+
* @param attribute Name of the attribute to handle.
|
|
1358
|
+
*/ registerAttribute(attribute) {
|
|
1138
1359
|
this.attributes.add(attribute);
|
|
1139
1360
|
}
|
|
1140
1361
|
/**
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1362
|
+
* Updates the document selection and the view according to the two–step caret movement state
|
|
1363
|
+
* when moving **forwards**. Executed upon `keypress` in the {@link module:engine/view/view~View}.
|
|
1364
|
+
*
|
|
1365
|
+
* @param data Data of the key press.
|
|
1366
|
+
* @returns `true` when the handler prevented caret movement.
|
|
1367
|
+
*/ _handleForwardMovement(data) {
|
|
1147
1368
|
const attributes = this.attributes;
|
|
1148
1369
|
const model = this.editor.model;
|
|
1149
1370
|
const selection = model.document.selection;
|
|
@@ -1194,12 +1415,12 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1194
1415
|
return false;
|
|
1195
1416
|
}
|
|
1196
1417
|
/**
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1418
|
+
* Updates the document selection and the view according to the two–step caret movement state
|
|
1419
|
+
* when moving **backwards**. Executed upon `keypress` in the {@link module:engine/view/view~View}.
|
|
1420
|
+
*
|
|
1421
|
+
* @param data Data of the key press.
|
|
1422
|
+
* @returns `true` when the handler prevented caret movement
|
|
1423
|
+
*/ _handleBackwardMovement(data) {
|
|
1203
1424
|
const attributes = this.attributes;
|
|
1204
1425
|
const model = this.editor.model;
|
|
1205
1426
|
const selection = model.document.selection;
|
|
@@ -1283,14 +1504,14 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1283
1504
|
return false;
|
|
1284
1505
|
}
|
|
1285
1506
|
/**
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1507
|
+
* Starts listening to {@link module:engine/view/document~Document#event:mousedown} and
|
|
1508
|
+
* {@link module:engine/view/document~Document#event:selectionChange} and puts the selection before/after a 2-step node
|
|
1509
|
+
* if clicked at the beginning/ending of the 2-step node.
|
|
1510
|
+
*
|
|
1511
|
+
* The purpose of this action is to allow typing around the 2-step node directly after a click.
|
|
1512
|
+
*
|
|
1513
|
+
* See https://github.com/ckeditor/ckeditor5/issues/1016.
|
|
1514
|
+
*/ _enableClickingAfterNode() {
|
|
1294
1515
|
const editor = this.editor;
|
|
1295
1516
|
const model = editor.model;
|
|
1296
1517
|
const selection = model.document.selection;
|
|
@@ -1333,14 +1554,14 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1333
1554
|
});
|
|
1334
1555
|
}
|
|
1335
1556
|
/**
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1557
|
+
* Starts listening to {@link module:engine/model/model~Model#event:insertContent} and corrects the model
|
|
1558
|
+
* selection attributes if the selection is at the end of a two-step node after inserting the content.
|
|
1559
|
+
*
|
|
1560
|
+
* The purpose of this action is to improve the overall UX because the user is no longer "trapped" by the
|
|
1561
|
+
* two-step attribute of the selection, and they can type a "clean" (`linkHref`–less) text right away.
|
|
1562
|
+
*
|
|
1563
|
+
* See https://github.com/ckeditor/ckeditor5/issues/6053.
|
|
1564
|
+
*/ _enableInsertContentSelectionAttributesFixer() {
|
|
1344
1565
|
const editor = this.editor;
|
|
1345
1566
|
const model = editor.model;
|
|
1346
1567
|
const selection = model.document.selection;
|
|
@@ -1355,17 +1576,17 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1355
1576
|
});
|
|
1356
1577
|
}
|
|
1357
1578
|
/**
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1579
|
+
* Starts listening to {@link module:engine/model/model~Model#deleteContent} and checks whether
|
|
1580
|
+
* removing a content right after the tow-step attribute.
|
|
1581
|
+
*
|
|
1582
|
+
* If so, the selection should not preserve the two-step attribute. However, if
|
|
1583
|
+
* the {@link module:typing/twostepcaretmovement~TwoStepCaretMovement} plugin is active and
|
|
1584
|
+
* the selection has the two-step attribute due to overridden gravity (at the end), the two-step attribute should stay untouched.
|
|
1585
|
+
*
|
|
1586
|
+
* The purpose of this action is to allow removing the link text and keep the selection outside the link.
|
|
1587
|
+
*
|
|
1588
|
+
* See https://github.com/ckeditor/ckeditor5/issues/7521.
|
|
1589
|
+
*/ _handleDeleteContentAfterNode() {
|
|
1369
1590
|
const editor = this.editor;
|
|
1370
1591
|
const model = editor.model;
|
|
1371
1592
|
const selection = model.document.selection;
|
|
@@ -1415,42 +1636,30 @@ class TwoStepCaretMovement extends Plugin {
|
|
|
1415
1636
|
});
|
|
1416
1637
|
}
|
|
1417
1638
|
/**
|
|
1418
|
-
|
|
1419
|
-
|
|
1639
|
+
* `true` when the gravity is overridden for the plugin.
|
|
1640
|
+
*/ get _isGravityOverridden() {
|
|
1420
1641
|
return !!this._overrideUid;
|
|
1421
1642
|
}
|
|
1422
1643
|
/**
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1644
|
+
* Overrides the gravity using the {@link module:engine/model/writer~Writer model writer}
|
|
1645
|
+
* and stores the information about this fact in the {@link #_overrideUid}.
|
|
1646
|
+
*
|
|
1647
|
+
* A shorthand for {@link module:engine/model/writer~Writer#overrideSelectionGravity}.
|
|
1648
|
+
*/ _overrideGravity() {
|
|
1428
1649
|
this._overrideUid = this.editor.model.change((writer)=>{
|
|
1429
1650
|
return writer.overrideSelectionGravity();
|
|
1430
1651
|
});
|
|
1431
1652
|
}
|
|
1432
1653
|
/**
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1654
|
+
* Restores the gravity using the {@link module:engine/model/writer~Writer model writer}.
|
|
1655
|
+
*
|
|
1656
|
+
* A shorthand for {@link module:engine/model/writer~Writer#restoreSelectionGravity}.
|
|
1657
|
+
*/ _restoreGravity() {
|
|
1437
1658
|
this.editor.model.change((writer)=>{
|
|
1438
1659
|
writer.restoreSelectionGravity(this._overrideUid);
|
|
1439
1660
|
this._overrideUid = null;
|
|
1440
1661
|
});
|
|
1441
1662
|
}
|
|
1442
|
-
/**
|
|
1443
|
-
* @inheritDoc
|
|
1444
|
-
*/ constructor(editor){
|
|
1445
|
-
super(editor);
|
|
1446
|
-
/**
|
|
1447
|
-
* A flag indicating that the automatic gravity restoration should not happen upon the next
|
|
1448
|
-
* gravity restoration.
|
|
1449
|
-
* {@link module:engine/model/selection~Selection#event:change:range} event.
|
|
1450
|
-
*/ this._isNextGravityRestorationSkipped = false;
|
|
1451
|
-
this.attributes = new Set();
|
|
1452
|
-
this._overrideUid = null;
|
|
1453
|
-
}
|
|
1454
1663
|
}
|
|
1455
1664
|
/**
|
|
1456
1665
|
* Checks whether the selection has any of given attributes.
|
|
@@ -1715,23 +1924,35 @@ const DEFAULT_TRANSFORMATIONS = [
|
|
|
1715
1924
|
'typography',
|
|
1716
1925
|
'quotes'
|
|
1717
1926
|
];
|
|
1718
|
-
|
|
1927
|
+
/**
|
|
1928
|
+
* The text transformation plugin.
|
|
1929
|
+
*/ class TextTransformation extends Plugin {
|
|
1719
1930
|
/**
|
|
1720
|
-
|
|
1721
|
-
|
|
1931
|
+
* @inheritDoc
|
|
1932
|
+
*/ static get requires() {
|
|
1722
1933
|
return [
|
|
1723
1934
|
'Delete',
|
|
1724
1935
|
'Input'
|
|
1725
1936
|
];
|
|
1726
1937
|
}
|
|
1727
1938
|
/**
|
|
1728
|
-
|
|
1729
|
-
|
|
1939
|
+
* @inheritDoc
|
|
1940
|
+
*/ static get pluginName() {
|
|
1730
1941
|
return 'TextTransformation';
|
|
1731
1942
|
}
|
|
1732
1943
|
/**
|
|
1733
|
-
|
|
1734
|
-
|
|
1944
|
+
* @inheritDoc
|
|
1945
|
+
*/ constructor(editor){
|
|
1946
|
+
super(editor);
|
|
1947
|
+
editor.config.define('typing', {
|
|
1948
|
+
transformations: {
|
|
1949
|
+
include: DEFAULT_TRANSFORMATIONS
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* @inheritDoc
|
|
1955
|
+
*/ init() {
|
|
1735
1956
|
const model = this.editor.model;
|
|
1736
1957
|
const modelSelection = model.document.selection;
|
|
1737
1958
|
modelSelection.on('change:range', ()=>{
|
|
@@ -1741,8 +1962,8 @@ class TextTransformation extends Plugin {
|
|
|
1741
1962
|
this._enableTransformationWatchers();
|
|
1742
1963
|
}
|
|
1743
1964
|
/**
|
|
1744
|
-
|
|
1745
|
-
|
|
1965
|
+
* Create new TextWatcher listening to the editor for typing and selection events.
|
|
1966
|
+
*/ _enableTransformationWatchers() {
|
|
1746
1967
|
const editor = this.editor;
|
|
1747
1968
|
const model = editor.model;
|
|
1748
1969
|
const deletePlugin = editor.plugins.get('Delete');
|
|
@@ -1789,16 +2010,6 @@ class TextTransformation extends Plugin {
|
|
|
1789
2010
|
});
|
|
1790
2011
|
watcher.bind('isEnabled').to(this);
|
|
1791
2012
|
}
|
|
1792
|
-
/**
|
|
1793
|
-
* @inheritDoc
|
|
1794
|
-
*/ constructor(editor){
|
|
1795
|
-
super(editor);
|
|
1796
|
-
editor.config.define('typing', {
|
|
1797
|
-
transformations: {
|
|
1798
|
-
include: DEFAULT_TRANSFORMATIONS
|
|
1799
|
-
}
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
2013
|
}
|
|
1803
2014
|
/**
|
|
1804
2015
|
* Normalizes the configuration `from` parameter value.
|
|
@@ -1876,6 +2087,8 @@ class TextTransformation extends Plugin {
|
|
|
1876
2087
|
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
1877
2088
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
1878
2089
|
*/ /**
|
|
2090
|
+
* @module typing/utils/findattributerange
|
|
2091
|
+
*/ /**
|
|
1879
2092
|
* Returns a model range that covers all consecutive nodes with the same `attributeName` and its `value`
|
|
1880
2093
|
* that intersect the given `position`.
|
|
1881
2094
|
*
|