@ckeditor/ckeditor5-typing 45.0.0 → 45.1.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/dist/index.js +143 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/delete.js +19 -0
- package/src/input.d.ts +1 -1
- package/src/input.js +116 -108
- package/src/inserttextobserver.d.ts +9 -0
- package/src/inserttextobserver.js +7 -5
package/dist/index.js
CHANGED
|
@@ -251,7 +251,7 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
251
251
|
if (!this.isEnabled) {
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
|
-
const { data: text, targetRanges, inputType, domEvent } = data;
|
|
254
|
+
const { data: text, targetRanges, inputType, domEvent, isComposing } = data;
|
|
255
255
|
if (!typingInputTypes.includes(inputType)) {
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
@@ -261,7 +261,8 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
261
261
|
const eventInfo = new EventInfo(viewDocument, 'insertText');
|
|
262
262
|
viewDocument.fire(eventInfo, new DomEventData(view, domEvent, {
|
|
263
263
|
text,
|
|
264
|
-
selection: view.createSelection(targetRanges)
|
|
264
|
+
selection: view.createSelection(targetRanges),
|
|
265
|
+
isComposing
|
|
265
266
|
}));
|
|
266
267
|
// Stop the beforeinput event if `delete` event was stopped.
|
|
267
268
|
// https://github.com/ckeditor/ckeditor5/issues/753
|
|
@@ -295,12 +296,13 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
295
296
|
// 1. The SelectionObserver is blocked and the view is not updated with the composition changes.
|
|
296
297
|
// 2. The last moment before it's locked is the `compositionstart` event.
|
|
297
298
|
// 3. The `SelectionObserver` is listening for `compositionstart` event and immediately converts
|
|
298
|
-
// the selection.
|
|
299
|
+
// the selection. Handle this at the low priority so after the rendering is blocked.
|
|
299
300
|
viewDocument.fire('insertText', new DomEventData(view, domEvent, {
|
|
300
|
-
text: data
|
|
301
|
+
text: data,
|
|
302
|
+
isComposing: true
|
|
301
303
|
}));
|
|
302
304
|
}, {
|
|
303
|
-
priority: '
|
|
305
|
+
priority: 'low'
|
|
304
306
|
});
|
|
305
307
|
}
|
|
306
308
|
}
|
|
@@ -318,7 +320,7 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
318
320
|
*/ class Input extends Plugin {
|
|
319
321
|
/**
|
|
320
322
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
321
|
-
*/
|
|
323
|
+
*/ _typingQueue;
|
|
322
324
|
/**
|
|
323
325
|
* @inheritDoc
|
|
324
326
|
*/ static get pluginName() {
|
|
@@ -337,25 +339,36 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
337
339
|
const view = editor.editing.view;
|
|
338
340
|
const mapper = editor.editing.mapper;
|
|
339
341
|
const modelSelection = model.document.selection;
|
|
340
|
-
this.
|
|
342
|
+
this._typingQueue = new TypingQueue(editor);
|
|
341
343
|
view.addObserver(InsertTextObserver);
|
|
342
344
|
// TODO The above default configuration value should be defined using editor.config.define() once it's fixed.
|
|
343
345
|
const insertTextCommand = new InsertTextCommand(editor, editor.config.get('typing.undoStep') || 20);
|
|
344
346
|
// Register `insertText` command and add `input` command as an alias for backward compatibility.
|
|
345
347
|
editor.commands.add('insertText', insertTextCommand);
|
|
346
348
|
editor.commands.add('input', insertTextCommand);
|
|
349
|
+
this.listenTo(view.document, 'beforeinput', ()=>{
|
|
350
|
+
// Flush queue on the next beforeinput event because it could happen
|
|
351
|
+
// that the mutation observer does not notice the DOM change in time.
|
|
352
|
+
this._typingQueue.flush('next beforeinput');
|
|
353
|
+
}, {
|
|
354
|
+
priority: 'high'
|
|
355
|
+
});
|
|
347
356
|
this.listenTo(view.document, 'insertText', (evt, data)=>{
|
|
348
|
-
|
|
349
|
-
//
|
|
350
|
-
if (
|
|
357
|
+
const { text, selection: viewSelection } = data;
|
|
358
|
+
// In case of a synthetic event, make sure that selection is not fake.
|
|
359
|
+
if (view.document.selection.isFake && viewSelection && view.document.selection.isSimilar(viewSelection)) {
|
|
351
360
|
data.preventDefault();
|
|
352
361
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
362
|
+
if (!insertTextCommand.isEnabled) {
|
|
363
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
364
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
365
|
+
// @if CK_DEBUG_TYPING // '%cInsertText command is disabled - prevent DOM change.',
|
|
366
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
367
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
368
|
+
// @if CK_DEBUG_TYPING // }
|
|
369
|
+
data.preventDefault();
|
|
370
|
+
return;
|
|
357
371
|
}
|
|
358
|
-
const { text, selection: viewSelection } = data;
|
|
359
372
|
let modelRanges;
|
|
360
373
|
// If view selection was specified, translate it to model selection.
|
|
361
374
|
if (viewSelection) {
|
|
@@ -394,40 +407,31 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
394
407
|
return;
|
|
395
408
|
}
|
|
396
409
|
}
|
|
410
|
+
// Note: the TypingQueue stores live-ranges internally as RTC could change the model while waiting for mutations.
|
|
397
411
|
const commandData = {
|
|
398
412
|
text: insertText,
|
|
399
413
|
selection: model.createSelection(modelRanges)
|
|
400
414
|
};
|
|
401
|
-
// This is a
|
|
415
|
+
// This is a beforeinput event, so we need to wait until the browser updates the DOM,
|
|
402
416
|
// and we could apply changes to the model and verify if the DOM is valid.
|
|
403
417
|
// The browser applies changes to the DOM not immediately on beforeinput event.
|
|
404
418
|
// We just wait for mutation observer to notice changes or as a fallback a timeout.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
// @if CK_DEBUG_TYPING // `%cExecute insertText:%c "${ commandData.text }"%c ` +
|
|
422
|
-
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getFirstPosition().path }]-` +
|
|
423
|
-
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getLastPosition().path }]`,
|
|
424
|
-
// @if CK_DEBUG_TYPING // 'font-weight: bold',
|
|
425
|
-
// @if CK_DEBUG_TYPING // 'color: blue',
|
|
426
|
-
// @if CK_DEBUG_TYPING // ''
|
|
427
|
-
// @if CK_DEBUG_TYPING // ) );
|
|
428
|
-
// @if CK_DEBUG_TYPING // }
|
|
429
|
-
editor.execute('insertText', commandData);
|
|
430
|
-
view.scrollToTheSelection();
|
|
419
|
+
//
|
|
420
|
+
// Previously we were cancelling the non-composition events, but it caused issues especially in Safari.
|
|
421
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
422
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
423
|
+
// @if CK_DEBUG_TYPING // `%cQueue insertText:%c "${ commandData.text }"%c ` +
|
|
424
|
+
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getFirstPosition().path }]-` +
|
|
425
|
+
// @if CK_DEBUG_TYPING // `[${ commandData.selection.getLastPosition().path }]` +
|
|
426
|
+
// @if CK_DEBUG_TYPING // ` queue size: ${ this._typingQueue.length + 1 }`,
|
|
427
|
+
// @if CK_DEBUG_TYPING // 'font-weight: bold',
|
|
428
|
+
// @if CK_DEBUG_TYPING // 'color: blue',
|
|
429
|
+
// @if CK_DEBUG_TYPING // ''
|
|
430
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
431
|
+
// @if CK_DEBUG_TYPING // }
|
|
432
|
+
this._typingQueue.push(commandData, Boolean(data.isComposing));
|
|
433
|
+
if (data.domEvent.defaultPrevented) {
|
|
434
|
+
this._typingQueue.flush('beforeinput default prevented');
|
|
431
435
|
}
|
|
432
436
|
});
|
|
433
437
|
// Delete selected content on composition start.
|
|
@@ -469,44 +473,48 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
469
473
|
// @if CK_DEBUG_TYPING // ) );
|
|
470
474
|
// @if CK_DEBUG_TYPING // }
|
|
471
475
|
deleteSelectionContent(model, insertTextCommand);
|
|
476
|
+
}, {
|
|
477
|
+
priority: 'high'
|
|
472
478
|
});
|
|
473
479
|
}
|
|
474
|
-
// Apply
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
this.
|
|
479
|
-
if (!view.document.isComposing) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
// Check if mutations are relevant for queued changes.
|
|
480
|
+
// Apply changes to the model as they are applied to the DOM by the browser.
|
|
481
|
+
// On beforeinput event, the DOM is not yet modified. We wait for detected mutations to apply model changes.
|
|
482
|
+
this.listenTo(view.document, 'mutations', (evt, { mutations })=>{
|
|
483
|
+
// Check if mutations are relevant for queued changes.
|
|
484
|
+
if (this._typingQueue.hasAffectedElements()) {
|
|
483
485
|
for (const { node } of mutations){
|
|
484
486
|
const viewElement = findMappedViewAncestor(node, mapper);
|
|
485
487
|
const modelElement = mapper.toModelElement(viewElement);
|
|
486
|
-
if (this.
|
|
487
|
-
this.
|
|
488
|
+
if (this._typingQueue.isElementAffected(modelElement)) {
|
|
489
|
+
this._typingQueue.flush('mutations');
|
|
488
490
|
return;
|
|
489
491
|
}
|
|
490
492
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
493
|
+
}
|
|
494
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
495
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
496
|
+
// @if CK_DEBUG_TYPING // '%cMutations not related to the composition.',
|
|
497
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
498
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
499
|
+
// @if CK_DEBUG_TYPING // }
|
|
500
|
+
});
|
|
501
|
+
// Make sure that all changes are applied to the model before the end of composition.
|
|
502
|
+
this.listenTo(view.document, 'compositionend', ()=>{
|
|
503
|
+
this._typingQueue.flush('before composition end');
|
|
504
|
+
}, {
|
|
505
|
+
priority: 'high'
|
|
506
|
+
});
|
|
507
|
+
// Trigger mutations check after the composition completes to fix all DOM changes that got ignored during composition.
|
|
508
|
+
// On Android, the Renderer is not disabled while composing. While updating DOM nodes, we ignore some changes
|
|
509
|
+
// that are not that important (like NBSP vs. plain space character) and could break the composition flow.
|
|
510
|
+
// After composition is completed, we trigger additional `mutations` event for elements affected by the composition
|
|
511
|
+
// so the Renderer can adjust the DOM to the expected structure without breaking the composition.
|
|
512
|
+
this.listenTo(view.document, 'compositionend', ()=>{
|
|
513
|
+
// There could be new item queued on the composition end, so flush it.
|
|
514
|
+
this._typingQueue.flush('after composition end');
|
|
515
|
+
const mutations = [];
|
|
516
|
+
if (this._typingQueue.hasAffectedElements()) {
|
|
517
|
+
for (const element of this._typingQueue.flushAffectedElements()){
|
|
510
518
|
const viewElement = mapper.toViewElement(element);
|
|
511
519
|
if (!viewElement) {
|
|
512
520
|
continue;
|
|
@@ -516,63 +524,49 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
516
524
|
node: viewElement
|
|
517
525
|
});
|
|
518
526
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// @if CK_DEBUG_TYPING // }
|
|
526
|
-
view.document.fire('mutations', {
|
|
527
|
-
mutations
|
|
528
|
-
});
|
|
529
|
-
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
530
|
-
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
531
|
-
// @if CK_DEBUG_TYPING // }
|
|
532
|
-
}
|
|
533
|
-
}, {
|
|
534
|
-
priority: 'lowest'
|
|
535
|
-
});
|
|
536
|
-
} else {
|
|
537
|
-
// After composition end we need to verify if there are no left-overs.
|
|
538
|
-
// Listening at the lowest priority so after the `InsertTextObserver` added above (all composed text
|
|
527
|
+
}
|
|
528
|
+
// Fire composition mutations, if any.
|
|
529
|
+
//
|
|
530
|
+
// For non-Android:
|
|
531
|
+
// After the composition end, we need to verify if there are no left-overs.
|
|
532
|
+
// Listening at the lowest priority, so after the `InsertTextObserver` added above (all composed text
|
|
539
533
|
// should already be applied to the model, view, and DOM).
|
|
540
|
-
// On non-Android the `Renderer` is blocked while user is composing but the `MutationObserver` still collects
|
|
534
|
+
// On non-Android the `Renderer` is blocked while the user is composing, but the `MutationObserver` still collects
|
|
541
535
|
// mutated nodes and fires `mutations` events.
|
|
542
536
|
// Those events are recorded by the `Renderer` but not applied to the DOM while composing.
|
|
543
537
|
// We need to trigger those checks (and fixes) once again but this time without specifying the exact mutations
|
|
544
538
|
// since they are already recorded by the `Renderer`.
|
|
545
|
-
// It in
|
|
539
|
+
// It in most cases just clears the internal record of mutated text nodes
|
|
546
540
|
// since all changes should already be applied to the DOM.
|
|
547
|
-
// This is especially needed when user cancels composition, so we can clear nodes marked to sync.
|
|
548
|
-
|
|
541
|
+
// This is especially needed when a user cancels composition, so we can clear nodes marked to sync.
|
|
542
|
+
if (mutations.length || !env.isAndroid) {
|
|
549
543
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
550
544
|
// @if CK_DEBUG_TYPING // console.group( ..._buildLogMessage( this, 'Input',
|
|
551
|
-
// @if CK_DEBUG_TYPING // '%
|
|
545
|
+
// @if CK_DEBUG_TYPING // '%cFire post-composition mutation fixes.',
|
|
552
546
|
// @if CK_DEBUG_TYPING // 'font-weight: bold'
|
|
553
547
|
// @if CK_DEBUG_TYPING // ) );
|
|
554
548
|
// @if CK_DEBUG_TYPING // }
|
|
555
549
|
view.document.fire('mutations', {
|
|
556
|
-
mutations
|
|
550
|
+
mutations
|
|
557
551
|
});
|
|
558
552
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
559
553
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
560
554
|
// @if CK_DEBUG_TYPING // }
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
555
|
+
}
|
|
556
|
+
}, {
|
|
557
|
+
priority: 'lowest'
|
|
558
|
+
});
|
|
565
559
|
}
|
|
566
560
|
/**
|
|
567
561
|
* @inheritDoc
|
|
568
562
|
*/ destroy() {
|
|
569
563
|
super.destroy();
|
|
570
|
-
this.
|
|
564
|
+
this._typingQueue.destroy();
|
|
571
565
|
}
|
|
572
566
|
}
|
|
573
567
|
/**
|
|
574
568
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
575
|
-
*/ class
|
|
569
|
+
*/ class TypingQueue {
|
|
576
570
|
/**
|
|
577
571
|
* The editor instance.
|
|
578
572
|
*/ editor;
|
|
@@ -583,8 +577,11 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
583
577
|
* The queue of `insertText` command executions that are waiting for the DOM to get updated after beforeinput event.
|
|
584
578
|
*/ _queue = [];
|
|
585
579
|
/**
|
|
586
|
-
*
|
|
587
|
-
*/
|
|
580
|
+
* Whether there is any composition enqueued or plain typing only.
|
|
581
|
+
*/ _isComposing = false;
|
|
582
|
+
/**
|
|
583
|
+
* A set of model elements. The typing happened in those elements. It's used for mutations check.
|
|
584
|
+
*/ _affectedElements = new Set();
|
|
588
585
|
/**
|
|
589
586
|
* @inheritDoc
|
|
590
587
|
*/ constructor(editor){
|
|
@@ -594,7 +591,7 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
594
591
|
* Destroys the helper object.
|
|
595
592
|
*/ destroy() {
|
|
596
593
|
this.flushDebounced.cancel();
|
|
597
|
-
this.
|
|
594
|
+
this._affectedElements.clear();
|
|
598
595
|
while(this._queue.length){
|
|
599
596
|
this.shift();
|
|
600
597
|
}
|
|
@@ -606,7 +603,7 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
606
603
|
}
|
|
607
604
|
/**
|
|
608
605
|
* Push next insertText command data to the queue.
|
|
609
|
-
*/ push(commandData) {
|
|
606
|
+
*/ push(commandData, isComposing) {
|
|
610
607
|
const commandLiveData = {
|
|
611
608
|
text: commandData.text
|
|
612
609
|
};
|
|
@@ -615,10 +612,11 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
615
612
|
for (const range of commandData.selection.getRanges()){
|
|
616
613
|
commandLiveData.selectionRanges.push(LiveRange.fromRange(range));
|
|
617
614
|
// Keep reference to the model element for later mutation checks.
|
|
618
|
-
this.
|
|
615
|
+
this._affectedElements.add(range.start.parent);
|
|
619
616
|
}
|
|
620
617
|
}
|
|
621
618
|
this._queue.push(commandLiveData);
|
|
619
|
+
this._isComposing ||= isComposing;
|
|
622
620
|
this.flushDebounced();
|
|
623
621
|
}
|
|
624
622
|
/**
|
|
@@ -673,6 +671,15 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
673
671
|
editor.execute('insertText', commandData);
|
|
674
672
|
}
|
|
675
673
|
buffer.unlock();
|
|
674
|
+
if (!this._isComposing) {
|
|
675
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
676
|
+
// @if CK_DEBUG_TYPING // console.log( ..._buildLogMessage( this, 'Input',
|
|
677
|
+
// @if CK_DEBUG_TYPING // 'Clear affected elements set'
|
|
678
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
679
|
+
// @if CK_DEBUG_TYPING // }
|
|
680
|
+
this._affectedElements.clear();
|
|
681
|
+
}
|
|
682
|
+
this._isComposing = false;
|
|
676
683
|
});
|
|
677
684
|
view.scrollToTheSelection();
|
|
678
685
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
@@ -680,15 +687,20 @@ const TYPING_INPUT_TYPES_ANDROID = [
|
|
|
680
687
|
// @if CK_DEBUG_TYPING // }
|
|
681
688
|
}
|
|
682
689
|
/**
|
|
683
|
-
* Returns `true` if the given model element is related to recent
|
|
684
|
-
*/
|
|
685
|
-
return this.
|
|
690
|
+
* Returns `true` if the given model element is related to recent typing.
|
|
691
|
+
*/ isElementAffected(element) {
|
|
692
|
+
return this._affectedElements.has(element);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Returns `true` if there are any affected elements in the queue.
|
|
696
|
+
*/ hasAffectedElements() {
|
|
697
|
+
return this._affectedElements.size > 0;
|
|
686
698
|
}
|
|
687
699
|
/**
|
|
688
|
-
* Returns an array of
|
|
689
|
-
*/
|
|
690
|
-
const result = Array.from(this.
|
|
691
|
-
this.
|
|
700
|
+
* Returns an array of typing-related elements and clears the internal list.
|
|
701
|
+
*/ flushAffectedElements() {
|
|
702
|
+
const result = Array.from(this._affectedElements);
|
|
703
|
+
this._affectedElements.clear();
|
|
692
704
|
return result;
|
|
693
705
|
}
|
|
694
706
|
}
|
|
@@ -1232,7 +1244,22 @@ const DELETE_EVENT_TYPES = {
|
|
|
1232
1244
|
const ancestorLimit = editor.model.schema.getLimitElement(modelDocument.selection);
|
|
1233
1245
|
const limitStartPosition = editor.model.createPositionAt(ancestorLimit, 0);
|
|
1234
1246
|
if (limitStartPosition.isTouching(modelDocument.selection.getFirstPosition())) {
|
|
1247
|
+
// Stop the beforeinput event as it could be invalid.
|
|
1235
1248
|
data.preventDefault();
|
|
1249
|
+
// Create a fake delete event so all features can act on it and the target range is proper.
|
|
1250
|
+
const modelRange = editor.model.schema.getNearestSelectionRange(limitStartPosition, 'forward');
|
|
1251
|
+
if (!modelRange) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const viewSelection = view.createSelection(editor.editing.mapper.toViewRange(modelRange));
|
|
1255
|
+
const targetRange = viewSelection.getFirstRange();
|
|
1256
|
+
const eventInfo = new BubblingEventInfo(document, 'delete', targetRange);
|
|
1257
|
+
const deleteData = {
|
|
1258
|
+
unit: 'selection',
|
|
1259
|
+
direction: 'backward',
|
|
1260
|
+
selectionToRemove: viewSelection
|
|
1261
|
+
};
|
|
1262
|
+
viewDocument.fire(eventInfo, new DomEventData(view, data.domEvent, deleteData));
|
|
1236
1263
|
}
|
|
1237
1264
|
});
|
|
1238
1265
|
if (this.editor.plugins.has('UndoEditing')) {
|