@ckeditor/ckeditor5-widget 0.0.0-nightly-20240509.0 → 0.0.0-nightly-20240511.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.

Potentially problematic release.


This version of @ckeditor/ckeditor5-widget might be problematic. Click here for more details.

package/dist/index.js CHANGED
@@ -10,12 +10,23 @@ import { IconView, Template, ContextualBalloon, ToolbarView, BalloonPanelView, V
10
10
  import { Enter } from '@ckeditor/ckeditor5-enter/dist/index.js';
11
11
  import { throttle } from 'lodash-es';
12
12
 
13
- class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
14
- /**
15
- * Adds highlight descriptor to the stack.
16
- *
17
- * @fires change:top
18
- */ add(descriptor, writer) {
13
+ /**
14
+ * Class used to handle the correct order of highlights on elements.
15
+ *
16
+ * When different highlights are applied to same element the correct order should be preserved:
17
+ *
18
+ * * highlight with highest priority should be applied,
19
+ * * if two highlights have same priority - sort by CSS class provided in
20
+ * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor}.
21
+ *
22
+ * This way, highlight will be applied with the same rules it is applied on texts.
23
+ */ class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
24
+ _stack = [];
25
+ /**
26
+ * Adds highlight descriptor to the stack.
27
+ *
28
+ * @fires change:top
29
+ */ add(descriptor, writer) {
19
30
  const stack = this._stack;
20
31
  // Save top descriptor and insert new one. If top is changed - fire event.
21
32
  const oldTop = stack[0];
@@ -31,11 +42,11 @@ class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
31
42
  }
32
43
  }
33
44
  /**
34
- * Removes highlight descriptor from the stack.
35
- *
36
- * @fires change:top
37
- * @param id Id of the descriptor to remove.
38
- */ remove(id, writer) {
45
+ * Removes highlight descriptor from the stack.
46
+ *
47
+ * @fires change:top
48
+ * @param id Id of the descriptor to remove.
49
+ */ remove(id, writer) {
39
50
  const stack = this._stack;
40
51
  const oldTop = stack[0];
41
52
  this._removeDescriptor(id);
@@ -50,9 +61,9 @@ class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
50
61
  }
51
62
  }
52
63
  /**
53
- * Inserts a given descriptor in correct place in the stack. It also takes care about updating information
54
- * when descriptor with same id is already present.
55
- */ _insertDescriptor(descriptor) {
64
+ * Inserts a given descriptor in correct place in the stack. It also takes care about updating information
65
+ * when descriptor with same id is already present.
66
+ */ _insertDescriptor(descriptor) {
56
67
  const stack = this._stack;
57
68
  const index = stack.findIndex((item)=>item.id === descriptor.id);
58
69
  // Inserting exact same descriptor - do nothing.
@@ -72,10 +83,10 @@ class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
72
83
  stack.splice(i, 0, descriptor);
73
84
  }
74
85
  /**
75
- * Removes descriptor with given id from the stack.
76
- *
77
- * @param id Descriptor's id.
78
- */ _removeDescriptor(id) {
86
+ * Removes descriptor with given id from the stack.
87
+ *
88
+ * @param id Descriptor's id.
89
+ */ _removeDescriptor(id) {
79
90
  const stack = this._stack;
80
91
  const index = stack.findIndex((item)=>item.id === id);
81
92
  // If descriptor with same id is on the list - remove it.
@@ -83,10 +94,6 @@ class HighlightStack extends /* #__PURE__ */ EmitterMixin() {
83
94
  stack.splice(index, 1);
84
95
  }
85
96
  }
86
- constructor(){
87
- super(...arguments);
88
- this._stack = [];
89
- }
90
97
  }
91
98
  /**
92
99
  * Compares two descriptors by checking their priority and class list.
@@ -177,12 +184,12 @@ var dragHandleIcon = "<svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/
177
184
  */ function toWidget(element, writer, options = {}) {
178
185
  if (!element.is('containerElement')) {
179
186
  /**
180
- * The element passed to `toWidget()` must be a {@link module:engine/view/containerelement~ContainerElement}
181
- * instance.
182
- *
183
- * @error widget-to-widget-wrong-element-type
184
- * @param element The view element passed to `toWidget()`.
185
- */ throw new CKEditorError('widget-to-widget-wrong-element-type', null, {
187
+ * The element passed to `toWidget()` must be a {@link module:engine/view/containerelement~ContainerElement}
188
+ * instance.
189
+ *
190
+ * @error widget-to-widget-wrong-element-type
191
+ * @param element The view element passed to `toWidget()`.
192
+ */ throw new CKEditorError('widget-to-widget-wrong-element-type', null, {
186
193
  element
187
194
  });
188
195
  }
@@ -538,23 +545,37 @@ const POSSIBLE_INSERTION_POSITIONS = [
538
545
  // Do the SVG parsing once and then clone the result <svg> DOM element for each new button.
539
546
  const RETURN_ARROW_ICON_ELEMENT = new DOMParser().parseFromString(returnIcon, 'image/svg+xml').firstChild;
540
547
  const PLUGIN_DISABLED_EDITING_ROOT_CLASS = 'ck-widget__type-around_disabled';
541
- class WidgetTypeAround extends Plugin {
542
- /**
543
- * @inheritDoc
544
- */ static get pluginName() {
548
+ /**
549
+ * A plugin that allows users to type around widgets where normally it is impossible to place the caret due
550
+ * to limitations of web browsers. These "tight spots" occur, for instance, before (or after) a widget being
551
+ * the first (or last) child of its parent or between two block widgets.
552
+ *
553
+ * This plugin extends the {@link module:widget/widget~Widget `Widget`} plugin and injects the user interface
554
+ * with two buttons into each widget instance in the editor. Each of the buttons can be clicked by the
555
+ * user if the widget is next to the "tight spot". Once clicked, a paragraph is created with the selection anchored
556
+ * in it so that users can type (or insert content, paste, etc.) straight away.
557
+ */ class WidgetTypeAround extends Plugin {
558
+ /**
559
+ * A reference to the model widget element that has the fake caret active
560
+ * on either side of it. It is later used to remove CSS classes associated with the fake caret
561
+ * when the widget no longer needs it.
562
+ */ _currentFakeCaretModelElement = null;
563
+ /**
564
+ * @inheritDoc
565
+ */ static get pluginName() {
545
566
  return 'WidgetTypeAround';
546
567
  }
547
568
  /**
548
- * @inheritDoc
549
- */ static get requires() {
569
+ * @inheritDoc
570
+ */ static get requires() {
550
571
  return [
551
572
  Enter,
552
573
  Delete
553
574
  ];
554
575
  }
555
576
  /**
556
- * @inheritDoc
557
- */ init() {
577
+ * @inheritDoc
578
+ */ init() {
558
579
  const editor = this.editor;
559
580
  const editingView = editor.editing.view;
560
581
  // Set a CSS class on the view editing root when the plugin is disabled so all the buttons
@@ -586,20 +607,20 @@ class WidgetTypeAround extends Plugin {
586
607
  this._enableDeleteContentIntegration();
587
608
  }
588
609
  /**
589
- * @inheritDoc
590
- */ destroy() {
610
+ * @inheritDoc
611
+ */ destroy() {
591
612
  super.destroy();
592
613
  this._currentFakeCaretModelElement = null;
593
614
  }
594
615
  /**
595
- * Inserts a new paragraph next to a widget element with the selection anchored in it.
596
- *
597
- * **Note**: This method is heavily user-oriented and will both focus the editing view and scroll
598
- * the viewport to the selection in the inserted paragraph.
599
- *
600
- * @param widgetModelElement The model widget element next to which a paragraph is inserted.
601
- * @param position The position where the paragraph is inserted. Either `'before'` or `'after'` the widget.
602
- */ _insertParagraph(widgetModelElement, position) {
616
+ * Inserts a new paragraph next to a widget element with the selection anchored in it.
617
+ *
618
+ * **Note**: This method is heavily user-oriented and will both focus the editing view and scroll
619
+ * the viewport to the selection in the inserted paragraph.
620
+ *
621
+ * @param widgetModelElement The model widget element next to which a paragraph is inserted.
622
+ * @param position The position where the paragraph is inserted. Either `'before'` or `'after'` the widget.
623
+ */ _insertParagraph(widgetModelElement, position) {
603
624
  const editor = this.editor;
604
625
  const editingView = editor.editing.view;
605
626
  const attributesToCopy = editor.model.schema.getAttributesWithProperty(widgetModelElement, 'copyOnReplace', true);
@@ -611,16 +632,16 @@ class WidgetTypeAround extends Plugin {
611
632
  editingView.scrollToTheSelection();
612
633
  }
613
634
  /**
614
- * A wrapper for the {@link module:utils/emittermixin~Emitter#listenTo} method that executes the callbacks only
615
- * when the plugin {@link #isEnabled is enabled}.
616
- *
617
- * @param emitter The object that fires the event.
618
- * @param event The name of the event.
619
- * @param callback The function to be called on event.
620
- * @param options Additional options.
621
- * @param options.priority The priority of this event callback. The higher the priority value the sooner
622
- * the callback will be fired. Events having the same priority are called in the order they were added.
623
- */ _listenToIfEnabled(emitter, event, callback, options) {
635
+ * A wrapper for the {@link module:utils/emittermixin~Emitter#listenTo} method that executes the callbacks only
636
+ * when the plugin {@link #isEnabled is enabled}.
637
+ *
638
+ * @param emitter The object that fires the event.
639
+ * @param event The name of the event.
640
+ * @param callback The function to be called on event.
641
+ * @param options Additional options.
642
+ * @param options.priority The priority of this event callback. The higher the priority value the sooner
643
+ * the callback will be fired. Events having the same priority are called in the order they were added.
644
+ */ _listenToIfEnabled(emitter, event, callback, options) {
624
645
  this.listenTo(emitter, event, (...args)=>{
625
646
  // Do not respond if the plugin is disabled.
626
647
  if (this.isEnabled) {
@@ -629,16 +650,16 @@ class WidgetTypeAround extends Plugin {
629
650
  }, options);
630
651
  }
631
652
  /**
632
- * Similar to {@link #_insertParagraph}, this method inserts a paragraph except that it
633
- * does not expect a position. Instead, it performs the insertion next to a selected widget
634
- * according to the `widget-type-around` model selection attribute value (fake caret position).
635
- *
636
- * Because this method requires the `widget-type-around` attribute to be set,
637
- * the insertion can only happen when the widget's fake caret is active (e.g. activated
638
- * using the keyboard).
639
- *
640
- * @returns Returns `true` when the paragraph was inserted (the attribute was present) and `false` otherwise.
641
- */ _insertParagraphAccordingToFakeCaretPosition() {
653
+ * Similar to {@link #_insertParagraph}, this method inserts a paragraph except that it
654
+ * does not expect a position. Instead, it performs the insertion next to a selected widget
655
+ * according to the `widget-type-around` model selection attribute value (fake caret position).
656
+ *
657
+ * Because this method requires the `widget-type-around` attribute to be set,
658
+ * the insertion can only happen when the widget's fake caret is active (e.g. activated
659
+ * using the keyboard).
660
+ *
661
+ * @returns Returns `true` when the paragraph was inserted (the attribute was present) and `false` otherwise.
662
+ */ _insertParagraphAccordingToFakeCaretPosition() {
642
663
  const editor = this.editor;
643
664
  const model = editor.model;
644
665
  const modelSelection = model.document.selection;
@@ -656,12 +677,12 @@ class WidgetTypeAround extends Plugin {
656
677
  return true;
657
678
  }
658
679
  /**
659
- * Creates a listener in the editing conversion pipeline that injects the widget type around
660
- * UI into every single widget instance created in the editor.
661
- *
662
- * The UI is delivered as a {@link module:engine/view/uielement~UIElement}
663
- * wrapper which renders DOM buttons that users can use to insert paragraphs.
664
- */ _enableTypeAroundUIInjection() {
680
+ * Creates a listener in the editing conversion pipeline that injects the widget type around
681
+ * UI into every single widget instance created in the editor.
682
+ *
683
+ * The UI is delivered as a {@link module:engine/view/uielement~UIElement}
684
+ * wrapper which renders DOM buttons that users can use to insert paragraphs.
685
+ */ _enableTypeAroundUIInjection() {
665
686
  const editor = this.editor;
666
687
  const schema = editor.model.schema;
667
688
  const t = editor.locale.t;
@@ -687,30 +708,30 @@ class WidgetTypeAround extends Plugin {
687
708
  });
688
709
  }
689
710
  /**
690
- * Brings support for the fake caret that appears when either:
691
- *
692
- * * the selection moves to a widget from a position next to it using arrow keys,
693
- * * the arrow key is pressed when the widget is already selected.
694
- *
695
- * The fake caret lets the user know that they can start typing or just press
696
- * <kbd>Enter</kbd> to insert a paragraph at the position next to a widget as suggested by the fake caret.
697
- *
698
- * The fake caret disappears when the user changes the selection or the editor
699
- * gets blurred.
700
- *
701
- * The whole idea is as follows:
702
- *
703
- * 1. A user does one of the 2 scenarios described at the beginning.
704
- * 2. The "keydown" listener is executed and the decision is made whether to show or hide the fake caret.
705
- * 3. If it should show up, the `widget-type-around` model selection attribute is set indicating
706
- * on which side of the widget it should appear.
707
- * 4. The selection dispatcher reacts to the selection attribute and sets CSS classes responsible for the
708
- * fake caret on the view widget.
709
- * 5. If the fake caret should disappear, the selection attribute is removed and the dispatcher
710
- * does the CSS class clean-up in the view.
711
- * 6. Additionally, `change:range` and `FocusTracker#isFocused` listeners also remove the selection
712
- * attribute (the former also removes widget CSS classes).
713
- */ _enableTypeAroundFakeCaretActivationUsingKeyboardArrows() {
711
+ * Brings support for the fake caret that appears when either:
712
+ *
713
+ * * the selection moves to a widget from a position next to it using arrow keys,
714
+ * * the arrow key is pressed when the widget is already selected.
715
+ *
716
+ * The fake caret lets the user know that they can start typing or just press
717
+ * <kbd>Enter</kbd> to insert a paragraph at the position next to a widget as suggested by the fake caret.
718
+ *
719
+ * The fake caret disappears when the user changes the selection or the editor
720
+ * gets blurred.
721
+ *
722
+ * The whole idea is as follows:
723
+ *
724
+ * 1. A user does one of the 2 scenarios described at the beginning.
725
+ * 2. The "keydown" listener is executed and the decision is made whether to show or hide the fake caret.
726
+ * 3. If it should show up, the `widget-type-around` model selection attribute is set indicating
727
+ * on which side of the widget it should appear.
728
+ * 4. The selection dispatcher reacts to the selection attribute and sets CSS classes responsible for the
729
+ * fake caret on the view widget.
730
+ * 5. If the fake caret should disappear, the selection attribute is removed and the dispatcher
731
+ * does the CSS class clean-up in the view.
732
+ * 6. Additionally, `change:range` and `FocusTracker#isFocused` listeners also remove the selection
733
+ * attribute (the former also removes widget CSS classes).
734
+ */ _enableTypeAroundFakeCaretActivationUsingKeyboardArrows() {
714
735
  const editor = this.editor;
715
736
  const model = editor.model;
716
737
  const modelSelection = model.document.selection;
@@ -798,17 +819,17 @@ class WidgetTypeAround extends Plugin {
798
819
  }
799
820
  }
800
821
  /**
801
- * A listener executed on each "keydown" in the view document, a part of
802
- * {@link #_enableTypeAroundFakeCaretActivationUsingKeyboardArrows}.
803
- *
804
- * It decides whether the arrow keypress should activate the fake caret or not (also whether it should
805
- * be deactivated).
806
- *
807
- * The fake caret activation is done by setting the `widget-type-around` model selection attribute
808
- * in this listener, and stopping and preventing the event that would normally be handled by the widget
809
- * plugin that is responsible for the regular keyboard navigation near/across all widgets (that
810
- * includes inline widgets, which are ignored by the widget type around plugin).
811
- */ _handleArrowKeyPress(evt, domEventData) {
822
+ * A listener executed on each "keydown" in the view document, a part of
823
+ * {@link #_enableTypeAroundFakeCaretActivationUsingKeyboardArrows}.
824
+ *
825
+ * It decides whether the arrow keypress should activate the fake caret or not (also whether it should
826
+ * be deactivated).
827
+ *
828
+ * The fake caret activation is done by setting the `widget-type-around` model selection attribute
829
+ * in this listener, and stopping and preventing the event that would normally be handled by the widget
830
+ * plugin that is responsible for the regular keyboard navigation near/across all widgets (that
831
+ * includes inline widgets, which are ignored by the widget type around plugin).
832
+ */ _handleArrowKeyPress(evt, domEventData) {
812
833
  const editor = this.editor;
813
834
  const model = editor.model;
814
835
  const modelSelection = model.document.selection;
@@ -833,15 +854,15 @@ class WidgetTypeAround extends Plugin {
833
854
  }
834
855
  }
835
856
  /**
836
- * Handles the keyboard navigation on "keydown" when a widget is currently selected and activates or deactivates
837
- * the fake caret for that widget, depending on the current value of the `widget-type-around` model
838
- * selection attribute and the direction of the pressed arrow key.
839
- *
840
- * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
841
- * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
842
- * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
843
- * process the event any further. Returns `false` otherwise.
844
- */ _handleArrowKeyPressOnSelectedWidget(isForward) {
857
+ * Handles the keyboard navigation on "keydown" when a widget is currently selected and activates or deactivates
858
+ * the fake caret for that widget, depending on the current value of the `widget-type-around` model
859
+ * selection attribute and the direction of the pressed arrow key.
860
+ *
861
+ * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
862
+ * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
863
+ * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
864
+ * process the event any further. Returns `false` otherwise.
865
+ */ _handleArrowKeyPressOnSelectedWidget(isForward) {
845
866
  const editor = this.editor;
846
867
  const model = editor.model;
847
868
  const modelSelection = model.document.selection;
@@ -870,19 +891,19 @@ class WidgetTypeAround extends Plugin {
870
891
  });
871
892
  }
872
893
  /**
873
- * Handles the keyboard navigation on "keydown" when **no** widget is selected but the selection is **directly** next
874
- * to one and upon the fake caret should become active for this widget upon arrow keypress
875
- * (AKA entering/selecting the widget).
876
- *
877
- * **Note**: This code mirrors the implementation from the widget plugin but also adds the selection attribute.
878
- * Unfortunately, there is no safe way to let the widget plugin do the selection part first and then just set the
879
- * selection attribute here in the widget type around plugin. This is why this code must duplicate some from the widget plugin.
880
- *
881
- * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
882
- * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
883
- * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
884
- * process the event any further. Returns `false` otherwise.
885
- */ _handleArrowKeyPressWhenSelectionNextToAWidget(isForward) {
894
+ * Handles the keyboard navigation on "keydown" when **no** widget is selected but the selection is **directly** next
895
+ * to one and upon the fake caret should become active for this widget upon arrow keypress
896
+ * (AKA entering/selecting the widget).
897
+ *
898
+ * **Note**: This code mirrors the implementation from the widget plugin but also adds the selection attribute.
899
+ * Unfortunately, there is no safe way to let the widget plugin do the selection part first and then just set the
900
+ * selection attribute here in the widget type around plugin. This is why this code must duplicate some from the widget plugin.
901
+ *
902
+ * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
903
+ * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
904
+ * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
905
+ * process the event any further. Returns `false` otherwise.
906
+ */ _handleArrowKeyPressWhenSelectionNextToAWidget(isForward) {
886
907
  const editor = this.editor;
887
908
  const model = editor.model;
888
909
  const schema = model.schema;
@@ -902,14 +923,14 @@ class WidgetTypeAround extends Plugin {
902
923
  return false;
903
924
  }
904
925
  /**
905
- * Handles the keyboard navigation on "keydown" when a widget is currently selected (together with some other content)
906
- * and the widget is the first or last element in the selection. It activates or deactivates the fake caret for that widget.
907
- *
908
- * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
909
- * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
910
- * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
911
- * process the event any further. Returns `false` otherwise.
912
- */ _handleArrowKeyPressWhenNonCollapsedSelection(isForward) {
926
+ * Handles the keyboard navigation on "keydown" when a widget is currently selected (together with some other content)
927
+ * and the widget is the first or last element in the selection. It activates or deactivates the fake caret for that widget.
928
+ *
929
+ * @param isForward `true` when the pressed arrow key was responsible for the forward model selection movement
930
+ * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
931
+ * @returns Returns `true` when the keypress was handled and no other keydown listener of the editor should
932
+ * process the event any further. Returns `false` otherwise.
933
+ */ _handleArrowKeyPressWhenNonCollapsedSelection(isForward) {
913
934
  const editor = this.editor;
914
935
  const model = editor.model;
915
936
  const schema = model.schema;
@@ -928,10 +949,10 @@ class WidgetTypeAround extends Plugin {
928
949
  return false;
929
950
  }
930
951
  /**
931
- * Registers a `mousedown` listener for the view document which intercepts events
932
- * coming from the widget type around UI, which happens when a user clicks one of the buttons
933
- * that insert a paragraph next to a widget.
934
- */ _enableInsertingParagraphsOnButtonClick() {
952
+ * Registers a `mousedown` listener for the view document which intercepts events
953
+ * coming from the widget type around UI, which happens when a user clicks one of the buttons
954
+ * that insert a paragraph next to a widget.
955
+ */ _enableInsertingParagraphsOnButtonClick() {
935
956
  const editor = this.editor;
936
957
  const editingView = editor.editing.view;
937
958
  this._listenToIfEnabled(editingView.document, 'mousedown', (evt, domEventData)=>{
@@ -948,18 +969,18 @@ class WidgetTypeAround extends Plugin {
948
969
  });
949
970
  }
950
971
  /**
951
- * Creates the <kbd>Enter</kbd> key listener on the view document that allows the user to insert a paragraph
952
- * near the widget when either:
953
- *
954
- * * The fake caret was first activated using the arrow keys,
955
- * * The entire widget is selected in the model.
956
- *
957
- * In the first case, the new paragraph is inserted according to the `widget-type-around` selection
958
- * attribute (see {@link #_handleArrowKeyPress}).
959
- *
960
- * In the second case, the new paragraph is inserted based on whether a soft (<kbd>Shift</kbd>+<kbd>Enter</kbd>) keystroke
961
- * was pressed or not.
962
- */ _enableInsertingParagraphsOnEnterKeypress() {
972
+ * Creates the <kbd>Enter</kbd> key listener on the view document that allows the user to insert a paragraph
973
+ * near the widget when either:
974
+ *
975
+ * * The fake caret was first activated using the arrow keys,
976
+ * * The entire widget is selected in the model.
977
+ *
978
+ * In the first case, the new paragraph is inserted according to the `widget-type-around` selection
979
+ * attribute (see {@link #_handleArrowKeyPress}).
980
+ *
981
+ * In the second case, the new paragraph is inserted based on whether a soft (<kbd>Shift</kbd>+<kbd>Enter</kbd>) keystroke
982
+ * was pressed or not.
983
+ */ _enableInsertingParagraphsOnEnterKeypress() {
963
984
  const editor = this.editor;
964
985
  const selection = editor.model.document.selection;
965
986
  const editingView = editor.editing.view;
@@ -990,18 +1011,18 @@ class WidgetTypeAround extends Plugin {
990
1011
  });
991
1012
  }
992
1013
  /**
993
- * Similar to the {@link #_enableInsertingParagraphsOnEnterKeypress}, it allows the user
994
- * to insert a paragraph next to a widget when the fake caret was activated using arrow
995
- * keys but it responds to typing instead of <kbd>Enter</kbd>.
996
- *
997
- * Listener enabled by this method will insert a new paragraph according to the `widget-type-around`
998
- * model selection attribute as the user simply starts typing, which creates the impression that the fake caret
999
- * behaves like a real one rendered by the browser (AKA your text appears where the caret was).
1000
- *
1001
- * **Note**: At the moment this listener creates 2 undo steps: one for the `insertParagraph` command
1002
- * and another one for actual typing. It is not a disaster but this may need to be fixed
1003
- * sooner or later.
1004
- */ _enableInsertingParagraphsOnTypingKeystroke() {
1014
+ * Similar to the {@link #_enableInsertingParagraphsOnEnterKeypress}, it allows the user
1015
+ * to insert a paragraph next to a widget when the fake caret was activated using arrow
1016
+ * keys but it responds to typing instead of <kbd>Enter</kbd>.
1017
+ *
1018
+ * Listener enabled by this method will insert a new paragraph according to the `widget-type-around`
1019
+ * model selection attribute as the user simply starts typing, which creates the impression that the fake caret
1020
+ * behaves like a real one rendered by the browser (AKA your text appears where the caret was).
1021
+ *
1022
+ * **Note**: At the moment this listener creates 2 undo steps: one for the `insertParagraph` command
1023
+ * and another one for actual typing. It is not a disaster but this may need to be fixed
1024
+ * sooner or later.
1025
+ */ _enableInsertingParagraphsOnTypingKeystroke() {
1005
1026
  const editor = this.editor;
1006
1027
  const viewDocument = editor.editing.view.document;
1007
1028
  // Note: The priority must precede the default Input plugin insertText handler.
@@ -1035,13 +1056,13 @@ class WidgetTypeAround extends Plugin {
1035
1056
  }
1036
1057
  }
1037
1058
  /**
1038
- * It creates a "delete" event listener on the view document to handle cases when the <kbd>Delete</kbd> or <kbd>Backspace</kbd>
1039
- * is pressed and the fake caret is currently active.
1040
- *
1041
- * The fake caret should create an illusion of a real browser caret so that when it appears before or after
1042
- * a widget, pressing <kbd>Delete</kbd> or <kbd>Backspace</kbd> should remove a widget or delete the content
1043
- * before or after a widget (depending on the content surrounding the widget).
1044
- */ _enableDeleteIntegration() {
1059
+ * It creates a "delete" event listener on the view document to handle cases when the <kbd>Delete</kbd> or <kbd>Backspace</kbd>
1060
+ * is pressed and the fake caret is currently active.
1061
+ *
1062
+ * The fake caret should create an illusion of a real browser caret so that when it appears before or after
1063
+ * a widget, pressing <kbd>Delete</kbd> or <kbd>Backspace</kbd> should remove a widget or delete the content
1064
+ * before or after a widget (depending on the content surrounding the widget).
1065
+ */ _enableDeleteIntegration() {
1045
1066
  const editor = this.editor;
1046
1067
  const editingView = editor.editing.view;
1047
1068
  const model = editor.model;
@@ -1106,11 +1127,11 @@ class WidgetTypeAround extends Plugin {
1106
1127
  });
1107
1128
  }
1108
1129
  /**
1109
- * Attaches the {@link module:engine/model/model~Model#event:insertContent} event listener that, for instance, allows the user to paste
1110
- * content near a widget when the fake caret is first activated using the arrow keys.
1111
- *
1112
- * The content is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
1113
- */ _enableInsertContentIntegration() {
1130
+ * Attaches the {@link module:engine/model/model~Model#event:insertContent} event listener that, for instance, allows the user to paste
1131
+ * content near a widget when the fake caret is first activated using the arrow keys.
1132
+ *
1133
+ * The content is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
1134
+ */ _enableInsertContentIntegration() {
1114
1135
  const editor = this.editor;
1115
1136
  const model = this.editor.model;
1116
1137
  const documentSelection = model.document.selection;
@@ -1136,12 +1157,12 @@ class WidgetTypeAround extends Plugin {
1136
1157
  });
1137
1158
  }
1138
1159
  /**
1139
- * Attaches the {@link module:engine/model/model~Model#event:insertObject} event listener that modifies the
1140
- * `options.findOptimalPosition`parameter to position of fake caret in relation to selected element
1141
- * to reflect user's intent of desired insertion position.
1142
- *
1143
- * The object is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
1144
- */ _enableInsertObjectIntegration() {
1160
+ * Attaches the {@link module:engine/model/model~Model#event:insertObject} event listener that modifies the
1161
+ * `options.findOptimalPosition`parameter to position of fake caret in relation to selected element
1162
+ * to reflect user's intent of desired insertion position.
1163
+ *
1164
+ * The object is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
1165
+ */ _enableInsertObjectIntegration() {
1145
1166
  const editor = this.editor;
1146
1167
  const model = this.editor.model;
1147
1168
  const documentSelection = model.document.selection;
@@ -1161,13 +1182,13 @@ class WidgetTypeAround extends Plugin {
1161
1182
  });
1162
1183
  }
1163
1184
  /**
1164
- * Attaches the {@link module:engine/model/model~Model#event:deleteContent} event listener to block the event when the fake
1165
- * caret is active.
1166
- *
1167
- * This is required for cases that trigger {@link module:engine/model/model~Model#deleteContent `model.deleteContent()`}
1168
- * before calling {@link module:engine/model/model~Model#insertContent `model.insertContent()`} like, for instance,
1169
- * plain text pasting.
1170
- */ _enableDeleteContentIntegration() {
1185
+ * Attaches the {@link module:engine/model/model~Model#event:deleteContent} event listener to block the event when the fake
1186
+ * caret is active.
1187
+ *
1188
+ * This is required for cases that trigger {@link module:engine/model/model~Model#deleteContent `model.deleteContent()`}
1189
+ * before calling {@link module:engine/model/model~Model#insertContent `model.insertContent()`} like, for instance,
1190
+ * plain text pasting.
1191
+ */ _enableDeleteContentIntegration() {
1171
1192
  const editor = this.editor;
1172
1193
  const model = this.editor.model;
1173
1194
  const documentSelection = model.document.selection;
@@ -1184,14 +1205,6 @@ class WidgetTypeAround extends Plugin {
1184
1205
  priority: 'high'
1185
1206
  });
1186
1207
  }
1187
- constructor(){
1188
- super(...arguments);
1189
- /**
1190
- * A reference to the model widget element that has the fake caret active
1191
- * on either side of it. It is later used to remove CSS classes associated with the fake caret
1192
- * when the widget no longer needs it.
1193
- */ this._currentFakeCaretModelElement = null;
1194
- }
1195
1208
  }
1196
1209
  /**
1197
1210
  * Injects the type around UI into a view widget instance.
@@ -1450,23 +1463,38 @@ function selectionWillShrink(selection, isForward) {
1450
1463
  return !selection.isCollapsed && selection.isBackward == isForward;
1451
1464
  }
1452
1465
 
1453
- class Widget extends Plugin {
1454
- /**
1455
- * @inheritDoc
1456
- */ static get pluginName() {
1466
+ /**
1467
+ * The widget plugin. It enables base support for widgets.
1468
+ *
1469
+ * See {@glink api/widget package page} for more details and documentation.
1470
+ *
1471
+ * This plugin enables multiple behaviors required by widgets:
1472
+ *
1473
+ * * The model to view selection converter for the editing pipeline (it handles widget custom selection rendering).
1474
+ * If a converted selection wraps around a widget element, that selection is marked as
1475
+ * {@link module:engine/view/selection~Selection#isFake fake}. Additionally, the `ck-widget_selected` CSS class
1476
+ * is added to indicate that widget has been selected.
1477
+ * * The mouse and keyboard events handling on and around widget elements.
1478
+ */ class Widget extends Plugin {
1479
+ /**
1480
+ * Holds previously selected widgets.
1481
+ */ _previouslySelected = new Set();
1482
+ /**
1483
+ * @inheritDoc
1484
+ */ static get pluginName() {
1457
1485
  return 'Widget';
1458
1486
  }
1459
1487
  /**
1460
- * @inheritDoc
1461
- */ static get requires() {
1488
+ * @inheritDoc
1489
+ */ static get requires() {
1462
1490
  return [
1463
1491
  WidgetTypeAround,
1464
1492
  Delete
1465
1493
  ];
1466
1494
  }
1467
1495
  /**
1468
- * @inheritDoc
1469
- */ init() {
1496
+ * @inheritDoc
1497
+ */ init() {
1470
1498
  const editor = this.editor;
1471
1499
  const view = editor.editing.view;
1472
1500
  const viewDocument = view.document;
@@ -1661,8 +1689,8 @@ class Widget extends Plugin {
1661
1689
  });
1662
1690
  }
1663
1691
  /**
1664
- * Handles {@link module:engine/view/document~Document#event:mousedown mousedown} events on widget elements.
1665
- */ _onMousedown(eventInfo, domEventData) {
1692
+ * Handles {@link module:engine/view/document~Document#event:mousedown mousedown} events on widget elements.
1693
+ */ _onMousedown(eventInfo, domEventData) {
1666
1694
  const editor = this.editor;
1667
1695
  const view = editor.editing.view;
1668
1696
  const viewDocument = view.document;
@@ -1699,8 +1727,8 @@ class Widget extends Plugin {
1699
1727
  this._setSelectionOverElement(modelElement);
1700
1728
  }
1701
1729
  /**
1702
- * Selects entire block content, e.g. on triple click it selects entire paragraph.
1703
- */ _selectBlockContent(element) {
1730
+ * Selects entire block content, e.g. on triple click it selects entire paragraph.
1731
+ */ _selectBlockContent(element) {
1704
1732
  const editor = this.editor;
1705
1733
  const model = editor.model;
1706
1734
  const mapper = editor.editing.mapper;
@@ -1719,14 +1747,14 @@ class Widget extends Plugin {
1719
1747
  return true;
1720
1748
  }
1721
1749
  /**
1722
- * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and changes
1723
- * the model selection when:
1724
- *
1725
- * * arrow key is pressed when the widget is selected,
1726
- * * the selection is next to a widget and the widget should become selected upon the arrow key press.
1727
- *
1728
- * See {@link #_preventDefaultOnArrowKeyPress}.
1729
- */ _handleSelectionChangeOnArrowKeyPress(eventInfo, domEventData) {
1750
+ * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and changes
1751
+ * the model selection when:
1752
+ *
1753
+ * * arrow key is pressed when the widget is selected,
1754
+ * * the selection is next to a widget and the widget should become selected upon the arrow key press.
1755
+ *
1756
+ * See {@link #_preventDefaultOnArrowKeyPress}.
1757
+ */ _handleSelectionChangeOnArrowKeyPress(eventInfo, domEventData) {
1730
1758
  const keyCode = domEventData.keyCode;
1731
1759
  const model = this.editor.model;
1732
1760
  const schema = model.schema;
@@ -1781,12 +1809,12 @@ class Widget extends Plugin {
1781
1809
  }
1782
1810
  }
1783
1811
  /**
1784
- * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and prevents
1785
- * the default browser behavior to make sure the fake selection is not being moved from a fake selection
1786
- * container.
1787
- *
1788
- * See {@link #_handleSelectionChangeOnArrowKeyPress}.
1789
- */ _preventDefaultOnArrowKeyPress(eventInfo, domEventData) {
1812
+ * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and prevents
1813
+ * the default browser behavior to make sure the fake selection is not being moved from a fake selection
1814
+ * container.
1815
+ *
1816
+ * See {@link #_handleSelectionChangeOnArrowKeyPress}.
1817
+ */ _preventDefaultOnArrowKeyPress(eventInfo, domEventData) {
1790
1818
  const model = this.editor.model;
1791
1819
  const schema = model.schema;
1792
1820
  const objectElement = model.document.selection.getSelectedElement();
@@ -1797,11 +1825,11 @@ class Widget extends Plugin {
1797
1825
  }
1798
1826
  }
1799
1827
  /**
1800
- * Handles delete keys: backspace and delete.
1801
- *
1802
- * @param isForward Set to true if delete was performed in forward direction.
1803
- * @returns Returns `true` if keys were handled correctly.
1804
- */ _handleDelete(isForward) {
1828
+ * Handles delete keys: backspace and delete.
1829
+ *
1830
+ * @param isForward Set to true if delete was performed in forward direction.
1831
+ * @returns Returns `true` if keys were handled correctly.
1832
+ */ _handleDelete(isForward) {
1805
1833
  const modelDocument = this.editor.model.document;
1806
1834
  const modelSelection = modelDocument.selection;
1807
1835
  // Do nothing when the read only mode is enabled.
@@ -1828,22 +1856,22 @@ class Widget extends Plugin {
1828
1856
  }
1829
1857
  }
1830
1858
  /**
1831
- * Sets {@link module:engine/model/selection~Selection document's selection} over given element.
1832
- *
1833
- * @internal
1834
- */ _setSelectionOverElement(element) {
1859
+ * Sets {@link module:engine/model/selection~Selection document's selection} over given element.
1860
+ *
1861
+ * @internal
1862
+ */ _setSelectionOverElement(element) {
1835
1863
  this.editor.model.change((writer)=>{
1836
1864
  writer.setSelection(writer.createRangeOn(element));
1837
1865
  });
1838
1866
  }
1839
1867
  /**
1840
- * Checks if {@link module:engine/model/element~Element element} placed next to the current
1841
- * {@link module:engine/model/selection~Selection model selection} exists and is marked in
1842
- * {@link module:engine/model/schema~Schema schema} as `object`.
1843
- *
1844
- * @internal
1845
- * @param forward Direction of checking.
1846
- */ _getObjectElementNextToSelection(forward) {
1868
+ * Checks if {@link module:engine/model/element~Element element} placed next to the current
1869
+ * {@link module:engine/model/selection~Selection model selection} exists and is marked in
1870
+ * {@link module:engine/model/schema~Schema schema} as `object`.
1871
+ *
1872
+ * @internal
1873
+ * @param forward Direction of checking.
1874
+ */ _getObjectElementNextToSelection(forward) {
1847
1875
  const model = this.editor.model;
1848
1876
  const schema = model.schema;
1849
1877
  const modelSelection = model.document.selection;
@@ -1864,16 +1892,16 @@ class Widget extends Plugin {
1864
1892
  return null;
1865
1893
  }
1866
1894
  /**
1867
- * Removes CSS class from previously selected widgets.
1868
- */ _clearPreviouslySelectedWidgets(writer) {
1895
+ * Removes CSS class from previously selected widgets.
1896
+ */ _clearPreviouslySelectedWidgets(writer) {
1869
1897
  for (const widget of this._previouslySelected){
1870
1898
  writer.removeClass(WIDGET_SELECTED_CLASS_NAME, widget);
1871
1899
  }
1872
1900
  this._previouslySelected.clear();
1873
1901
  }
1874
1902
  /**
1875
- * Moves the document selection into the first nested editable.
1876
- */ _selectFirstNestedEditable() {
1903
+ * Moves the document selection into the first nested editable.
1904
+ */ _selectFirstNestedEditable() {
1877
1905
  const editor = this.editor;
1878
1906
  const view = this.editor.editing.view;
1879
1907
  const viewDocument = view.document;
@@ -1894,8 +1922,8 @@ class Widget extends Plugin {
1894
1922
  return false;
1895
1923
  }
1896
1924
  /**
1897
- * Updates the document selection so that it selects first ancestor widget.
1898
- */ _selectAncestorWidget() {
1925
+ * Updates the document selection so that it selects first ancestor widget.
1926
+ */ _selectAncestorWidget() {
1899
1927
  const editor = this.editor;
1900
1928
  const mapper = editor.editing.mapper;
1901
1929
  const selection = editor.editing.view.document.selection;
@@ -1914,12 +1942,6 @@ class Widget extends Plugin {
1914
1942
  });
1915
1943
  return true;
1916
1944
  }
1917
- constructor(){
1918
- super(...arguments);
1919
- /**
1920
- * Holds previously selected widgets.
1921
- */ this._previouslySelected = new Set();
1922
- }
1923
1945
  }
1924
1946
  /**
1925
1947
  * Returns `true` when element is a nested editable or is placed inside one.
@@ -1982,22 +2004,51 @@ class Widget extends Plugin {
1982
2004
  return null;
1983
2005
  }
1984
2006
 
1985
- class WidgetToolbarRepository extends Plugin {
2007
+ /**
2008
+ * Widget toolbar repository plugin. A central point for registering widget toolbars. This plugin handles the whole
2009
+ * toolbar rendering process and exposes a concise API.
2010
+ *
2011
+ * To add a toolbar for your widget use the {@link ~WidgetToolbarRepository#register `WidgetToolbarRepository#register()`} method.
2012
+ *
2013
+ * The following example comes from the {@link module:image/imagetoolbar~ImageToolbar} plugin:
2014
+ *
2015
+ * ```ts
2016
+ * class ImageToolbar extends Plugin {
2017
+ * static get requires() {
2018
+ * return [ WidgetToolbarRepository ];
2019
+ * }
2020
+ *
2021
+ * afterInit() {
2022
+ * const editor = this.editor;
2023
+ * const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
2024
+ *
2025
+ * widgetToolbarRepository.register( 'image', {
2026
+ * items: editor.config.get( 'image.toolbar' ),
2027
+ * getRelatedElement: getClosestSelectedImageWidget
2028
+ * } );
2029
+ * }
2030
+ * }
2031
+ * ```
2032
+ */ class WidgetToolbarRepository extends Plugin {
2033
+ /**
2034
+ * A map of toolbar definitions.
2035
+ */ _toolbarDefinitions = new Map();
2036
+ _balloon;
1986
2037
  /**
1987
- * @inheritDoc
1988
- */ static get requires() {
2038
+ * @inheritDoc
2039
+ */ static get requires() {
1989
2040
  return [
1990
2041
  ContextualBalloon
1991
2042
  ];
1992
2043
  }
1993
2044
  /**
1994
- * @inheritDoc
1995
- */ static get pluginName() {
2045
+ * @inheritDoc
2046
+ */ static get pluginName() {
1996
2047
  return 'WidgetToolbarRepository';
1997
2048
  }
1998
2049
  /**
1999
- * @inheritDoc
2000
- */ init() {
2050
+ * @inheritDoc
2051
+ */ init() {
2001
2052
  const editor = this.editor;
2002
2053
  // Disables the default balloon toolbar for all widgets.
2003
2054
  if (editor.plugins.has('BalloonToolbar')) {
@@ -2031,35 +2082,35 @@ class WidgetToolbarRepository extends Plugin {
2031
2082
  }
2032
2083
  }
2033
2084
  /**
2034
- * Registers toolbar in the WidgetToolbarRepository. It renders it in the `ContextualBalloon` based on the value of the invoked
2035
- * `getRelatedElement` function. Toolbar items are gathered from `items` array.
2036
- * The balloon's CSS class is by default `ck-toolbar-container` and may be override with the `balloonClassName` option.
2037
- *
2038
- * Note: This method should be called in the {@link module:core/plugin~PluginInterface#afterInit `Plugin#afterInit()`}
2039
- * callback (or later) to make sure that the given toolbar items were already registered by other plugins.
2040
- *
2041
- * @param toolbarId An id for the toolbar. Used to
2042
- * @param options.ariaLabel Label used by assistive technologies to describe this toolbar element.
2043
- * @param options.items Array of toolbar items.
2044
- * @param options.getRelatedElement Callback which returns an element the toolbar should be attached to.
2045
- * @param options.balloonClassName CSS class for the widget balloon.
2046
- */ register(toolbarId, { ariaLabel, items, getRelatedElement, balloonClassName = 'ck-toolbar-container' }) {
2085
+ * Registers toolbar in the WidgetToolbarRepository. It renders it in the `ContextualBalloon` based on the value of the invoked
2086
+ * `getRelatedElement` function. Toolbar items are gathered from `items` array.
2087
+ * The balloon's CSS class is by default `ck-toolbar-container` and may be override with the `balloonClassName` option.
2088
+ *
2089
+ * Note: This method should be called in the {@link module:core/plugin~PluginInterface#afterInit `Plugin#afterInit()`}
2090
+ * callback (or later) to make sure that the given toolbar items were already registered by other plugins.
2091
+ *
2092
+ * @param toolbarId An id for the toolbar. Used to
2093
+ * @param options.ariaLabel Label used by assistive technologies to describe this toolbar element.
2094
+ * @param options.items Array of toolbar items.
2095
+ * @param options.getRelatedElement Callback which returns an element the toolbar should be attached to.
2096
+ * @param options.balloonClassName CSS class for the widget balloon.
2097
+ */ register(toolbarId, { ariaLabel, items, getRelatedElement, balloonClassName = 'ck-toolbar-container' }) {
2047
2098
  // Trying to register a toolbar without any item.
2048
2099
  if (!items.length) {
2049
2100
  /**
2050
- * When {@link module:widget/widgettoolbarrepository~WidgetToolbarRepository#register registering} a new widget toolbar, you
2051
- * need to provide a non-empty array with the items that will be inserted into the toolbar.
2052
- *
2053
- * If you see this error when integrating the editor, you likely forgot to configure one of the widget toolbars.
2054
- *
2055
- * See for instance:
2056
- *
2057
- * * {@link module:table/tableconfig~TableConfig#contentToolbar `config.table.contentToolbar`}
2058
- * * {@link module:image/imageconfig~ImageConfig#toolbar `config.image.toolbar`}
2059
- *
2060
- * @error widget-toolbar-no-items
2061
- * @param toolbarId The id of the toolbar that has not been configured correctly.
2062
- */ logWarning('widget-toolbar-no-items', {
2101
+ * When {@link module:widget/widgettoolbarrepository~WidgetToolbarRepository#register registering} a new widget toolbar, you
2102
+ * need to provide a non-empty array with the items that will be inserted into the toolbar.
2103
+ *
2104
+ * If you see this error when integrating the editor, you likely forgot to configure one of the widget toolbars.
2105
+ *
2106
+ * See for instance:
2107
+ *
2108
+ * * {@link module:table/tableconfig~TableConfig#contentToolbar `config.table.contentToolbar`}
2109
+ * * {@link module:image/imageconfig~ImageConfig#toolbar `config.image.toolbar`}
2110
+ *
2111
+ * @error widget-toolbar-no-items
2112
+ * @param toolbarId The id of the toolbar that has not been configured correctly.
2113
+ */ logWarning('widget-toolbar-no-items', {
2063
2114
  toolbarId
2064
2115
  });
2065
2116
  return;
@@ -2070,11 +2121,11 @@ class WidgetToolbarRepository extends Plugin {
2070
2121
  toolbarView.ariaLabel = ariaLabel || t('Widget toolbar');
2071
2122
  if (this._toolbarDefinitions.has(toolbarId)) {
2072
2123
  /**
2073
- * Toolbar with the given id was already added.
2074
- *
2075
- * @error widget-toolbar-duplicated
2076
- * @param toolbarId Toolbar id.
2077
- */ throw new CKEditorError('widget-toolbar-duplicated', this, {
2124
+ * Toolbar with the given id was already added.
2125
+ *
2126
+ * @error widget-toolbar-duplicated
2127
+ * @param toolbarId Toolbar id.
2128
+ */ throw new CKEditorError('widget-toolbar-duplicated', this, {
2078
2129
  toolbarId
2079
2130
  });
2080
2131
  }
@@ -2101,8 +2152,8 @@ class WidgetToolbarRepository extends Plugin {
2101
2152
  this._toolbarDefinitions.set(toolbarId, toolbarDefinition);
2102
2153
  }
2103
2154
  /**
2104
- * Iterates over stored toolbars and makes them visible or hidden.
2105
- */ _updateToolbarsVisibility() {
2155
+ * Iterates over stored toolbars and makes them visible or hidden.
2156
+ */ _updateToolbarsVisibility() {
2106
2157
  let maxRelatedElementDepth = 0;
2107
2158
  let deepestRelatedElement = null;
2108
2159
  let deepestToolbarDefinition = null;
@@ -2134,18 +2185,18 @@ class WidgetToolbarRepository extends Plugin {
2134
2185
  }
2135
2186
  }
2136
2187
  /**
2137
- * Hides the given toolbar.
2138
- */ _hideToolbar(toolbarDefinition) {
2188
+ * Hides the given toolbar.
2189
+ */ _hideToolbar(toolbarDefinition) {
2139
2190
  this._balloon.remove(toolbarDefinition.view);
2140
2191
  this.stopListening(this._balloon, 'change:visibleView');
2141
2192
  }
2142
2193
  /**
2143
- * Shows up the toolbar if the toolbar is not visible.
2144
- * Otherwise, repositions the toolbar's balloon when toolbar's view is the most top view in balloon stack.
2145
- *
2146
- * It might happen here that the toolbar's view is under another view. Then do nothing as the other toolbar view
2147
- * should be still visible after the {@link module:ui/editorui/editorui~EditorUI#event:update}.
2148
- */ _showToolbar(toolbarDefinition, relatedElement) {
2194
+ * Shows up the toolbar if the toolbar is not visible.
2195
+ * Otherwise, repositions the toolbar's balloon when toolbar's view is the most top view in balloon stack.
2196
+ *
2197
+ * It might happen here that the toolbar's view is under another view. Then do nothing as the other toolbar view
2198
+ * should be still visible after the {@link module:ui/editorui/editorui~EditorUI#event:update}.
2199
+ */ _showToolbar(toolbarDefinition, relatedElement) {
2149
2200
  if (this._isToolbarVisible(toolbarDefinition)) {
2150
2201
  repositionContextualBalloon(this.editor, relatedElement);
2151
2202
  } else if (!this._isToolbarInBalloon(toolbarDefinition)) {
@@ -2178,12 +2229,6 @@ class WidgetToolbarRepository extends Plugin {
2178
2229
  _isToolbarInBalloon(toolbar) {
2179
2230
  return this._balloon.hasView(toolbar.view);
2180
2231
  }
2181
- constructor(){
2182
- super(...arguments);
2183
- /**
2184
- * A map of toolbar definitions.
2185
- */ this._toolbarDefinitions = new Map();
2186
- }
2187
2232
  }
2188
2233
  function repositionContextualBalloon(editor, relatedElement) {
2189
2234
  const balloon = editor.plugins.get('ContextualBalloon');
@@ -2211,31 +2256,76 @@ function isWidgetSelected(selection) {
2211
2256
  return !!(viewElement && isWidget(viewElement));
2212
2257
  }
2213
2258
 
2214
- class ResizeState extends /* #__PURE__ */ ObservableMixin() {
2259
+ /**
2260
+ * Stores the internal state of a single resizable object.
2261
+ */ class ResizeState extends /* #__PURE__ */ ObservableMixin() {
2262
+ /**
2263
+ * The reference point of the resizer where the dragging started. It is used to measure the distance the user cursor
2264
+ * traveled, so how much the image should be enlarged.
2265
+ * This information is only known after the DOM was rendered, so it will be updated later.
2266
+ *
2267
+ * @internal
2268
+ */ _referenceCoordinates;
2269
+ /**
2270
+ * Resizer options.
2271
+ */ _options;
2272
+ /**
2273
+ * The original width (pixels) of the resized object when the resize process was started.
2274
+ *
2275
+ * @readonly
2276
+ */ _originalWidth;
2277
+ /**
2278
+ * The original height (pixels) of the resized object when the resize process was started.
2279
+ *
2280
+ * @readonly
2281
+ */ _originalHeight;
2282
+ /**
2283
+ * The original width (percents) of the resized object when the resize process was started.
2284
+ *
2285
+ * @readonly
2286
+ */ _originalWidthPercents;
2287
+ /**
2288
+ * A width to height ratio of the resized image.
2289
+ *
2290
+ * @readonly
2291
+ */ _aspectRatio;
2292
+ /**
2293
+ * @param options Resizer options.
2294
+ */ constructor(options){
2295
+ super();
2296
+ this.set('activeHandlePosition', null);
2297
+ this.set('proposedWidthPercents', null);
2298
+ this.set('proposedWidth', null);
2299
+ this.set('proposedHeight', null);
2300
+ this.set('proposedHandleHostWidth', null);
2301
+ this.set('proposedHandleHostHeight', null);
2302
+ this._options = options;
2303
+ this._referenceCoordinates = null;
2304
+ }
2215
2305
  /**
2216
- * The original width (pixels) of the resized object when the resize process was started.
2217
- */ get originalWidth() {
2306
+ * The original width (pixels) of the resized object when the resize process was started.
2307
+ */ get originalWidth() {
2218
2308
  return this._originalWidth;
2219
2309
  }
2220
2310
  /**
2221
- * The original height (pixels) of the resized object when the resize process was started.
2222
- */ get originalHeight() {
2311
+ * The original height (pixels) of the resized object when the resize process was started.
2312
+ */ get originalHeight() {
2223
2313
  return this._originalHeight;
2224
2314
  }
2225
2315
  /**
2226
- * The original width (percents) of the resized object when the resize process was started.
2227
- */ get originalWidthPercents() {
2316
+ * The original width (percents) of the resized object when the resize process was started.
2317
+ */ get originalWidthPercents() {
2228
2318
  return this._originalWidthPercents;
2229
2319
  }
2230
2320
  /**
2231
- * A width to height ratio of the resized image.
2232
- */ get aspectRatio() {
2321
+ * A width to height ratio of the resized image.
2322
+ */ get aspectRatio() {
2233
2323
  return this._aspectRatio;
2234
2324
  }
2235
2325
  /**
2236
- *
2237
- * @param domResizeHandle The handle used to calculate the reference point.
2238
- */ begin(domResizeHandle, domHandleHost, domResizeHost) {
2326
+ *
2327
+ * @param domResizeHandle The handle used to calculate the reference point.
2328
+ */ begin(domResizeHandle, domHandleHost, domResizeHost) {
2239
2329
  const clientRect = new Rect(domHandleHost);
2240
2330
  this.activeHandlePosition = getHandlePosition(domResizeHandle);
2241
2331
  this._referenceCoordinates = getAbsoluteBoundaryPoint(domHandleHost, getOppositePosition(this.activeHandlePosition));
@@ -2256,19 +2346,6 @@ class ResizeState extends /* #__PURE__ */ ObservableMixin() {
2256
2346
  this.proposedHandleHostWidth = newSize.handleHostWidth;
2257
2347
  this.proposedHandleHostHeight = newSize.handleHostHeight;
2258
2348
  }
2259
- /**
2260
- * @param options Resizer options.
2261
- */ constructor(options){
2262
- super();
2263
- this.set('activeHandlePosition', null);
2264
- this.set('proposedWidthPercents', null);
2265
- this.set('proposedWidth', null);
2266
- this.set('proposedHeight', null);
2267
- this.set('proposedHandleHostWidth', null);
2268
- this.set('proposedHandleHostHeight', null);
2269
- this._options = options;
2270
- this._referenceCoordinates = null;
2271
- }
2272
2349
  }
2273
2350
  /**
2274
2351
  * Returns coordinates of the top-left corner of an element, relative to the document's top-left corner.
@@ -2323,33 +2400,9 @@ class ResizeState extends /* #__PURE__ */ ObservableMixin() {
2323
2400
  return `${replacements[parts[0]]}-${replacements[parts[1]]}`;
2324
2401
  }
2325
2402
 
2326
- class SizeView extends View {
2327
- /**
2328
- * A method used for binding the `SizeView` instance properties to the `ResizeState` instance observable properties.
2329
- *
2330
- * @internal
2331
- * @param options An object defining the resizer options, used for setting the proper size label.
2332
- * @param resizeState The `ResizeState` class instance, used for keeping the `SizeView` state up to date.
2333
- */ _bindToState(options, resizeState) {
2334
- this.bind('_isVisible').to(resizeState, 'proposedWidth', resizeState, 'proposedHeight', (width, height)=>width !== null && height !== null);
2335
- this.bind('_label').to(resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', resizeState, 'proposedWidthPercents', (width, height, widthPercents)=>{
2336
- if (options.unit === 'px') {
2337
- return `${width}×${height}`;
2338
- } else {
2339
- return `${widthPercents}%`;
2340
- }
2341
- });
2342
- this.bind('_viewPosition').to(resizeState, 'activeHandlePosition', resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', // If the widget is too small to contain the size label, display the label above.
2343
- (position, width, height)=>width < 50 || height < 50 ? 'above-center' : position);
2344
- }
2345
- /**
2346
- * A method used for cleaning up. It removes the bindings and hides the view.
2347
- *
2348
- * @internal
2349
- */ _dismiss() {
2350
- this.unbind();
2351
- this._isVisible = false;
2352
- }
2403
+ /**
2404
+ * A view displaying the proposed new element size during the resizing.
2405
+ */ class SizeView extends View {
2353
2406
  constructor(){
2354
2407
  super();
2355
2408
  const bind = this.bindTemplate;
@@ -2372,35 +2425,103 @@ class SizeView extends View {
2372
2425
  ]
2373
2426
  });
2374
2427
  }
2428
+ /**
2429
+ * A method used for binding the `SizeView` instance properties to the `ResizeState` instance observable properties.
2430
+ *
2431
+ * @internal
2432
+ * @param options An object defining the resizer options, used for setting the proper size label.
2433
+ * @param resizeState The `ResizeState` class instance, used for keeping the `SizeView` state up to date.
2434
+ */ _bindToState(options, resizeState) {
2435
+ this.bind('_isVisible').to(resizeState, 'proposedWidth', resizeState, 'proposedHeight', (width, height)=>width !== null && height !== null);
2436
+ this.bind('_label').to(resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', resizeState, 'proposedWidthPercents', (width, height, widthPercents)=>{
2437
+ if (options.unit === 'px') {
2438
+ return `${width}×${height}`;
2439
+ } else {
2440
+ return `${widthPercents}%`;
2441
+ }
2442
+ });
2443
+ this.bind('_viewPosition').to(resizeState, 'activeHandlePosition', resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', // If the widget is too small to contain the size label, display the label above.
2444
+ (position, width, height)=>width < 50 || height < 50 ? 'above-center' : position);
2445
+ }
2446
+ /**
2447
+ * A method used for cleaning up. It removes the bindings and hides the view.
2448
+ *
2449
+ * @internal
2450
+ */ _dismiss() {
2451
+ this.unbind();
2452
+ this._isVisible = false;
2453
+ }
2375
2454
  }
2376
2455
 
2377
- class Resizer extends /* #__PURE__ */ ObservableMixin() {
2456
+ /**
2457
+ * Represents a resizer for a single resizable object.
2458
+ */ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2459
+ /**
2460
+ * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
2461
+ *
2462
+ * Note that a new state is created for each resize transaction.
2463
+ */ _state;
2378
2464
  /**
2379
- * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
2380
- *
2381
- * Note that a new state is created for each resize transaction.
2382
- */ get state() {
2465
+ * A view displaying the proposed new element size during the resizing.
2466
+ */ _sizeView;
2467
+ /**
2468
+ * Options passed to the {@link #constructor}.
2469
+ */ _options;
2470
+ /**
2471
+ * A wrapper that is controlled by the resizer. This is usually a widget element.
2472
+ */ _viewResizerWrapper = null;
2473
+ /**
2474
+ * The width of the resized {@link module:widget/widgetresize~ResizerOptions#viewElement viewElement} before the resizing started.
2475
+ */ _initialViewWidth;
2476
+ /**
2477
+ * @param options Resizer options.
2478
+ */ constructor(options){
2479
+ super();
2480
+ this._options = options;
2481
+ this.set('isEnabled', true);
2482
+ this.set('isSelected', false);
2483
+ this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected)=>isEnabled && isSelected);
2484
+ this.decorate('begin');
2485
+ this.decorate('cancel');
2486
+ this.decorate('commit');
2487
+ this.decorate('updateSize');
2488
+ this.on('commit', (event)=>{
2489
+ // State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
2490
+ // cleaned up (#5195).
2491
+ if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
2492
+ this._cleanup();
2493
+ event.stop();
2494
+ }
2495
+ }, {
2496
+ priority: 'high'
2497
+ });
2498
+ }
2499
+ /**
2500
+ * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
2501
+ *
2502
+ * Note that a new state is created for each resize transaction.
2503
+ */ get state() {
2383
2504
  return this._state;
2384
2505
  }
2385
2506
  /**
2386
- * Makes resizer visible in the UI.
2387
- */ show() {
2507
+ * Makes resizer visible in the UI.
2508
+ */ show() {
2388
2509
  const editingView = this._options.editor.editing.view;
2389
2510
  editingView.change((writer)=>{
2390
2511
  writer.removeClass('ck-hidden', this._viewResizerWrapper);
2391
2512
  });
2392
2513
  }
2393
2514
  /**
2394
- * Hides resizer in the UI.
2395
- */ hide() {
2515
+ * Hides resizer in the UI.
2516
+ */ hide() {
2396
2517
  const editingView = this._options.editor.editing.view;
2397
2518
  editingView.change((writer)=>{
2398
2519
  writer.addClass('ck-hidden', this._viewResizerWrapper);
2399
2520
  });
2400
2521
  }
2401
2522
  /**
2402
- * Attaches the resizer to the DOM.
2403
- */ attach() {
2523
+ * Attaches the resizer to the DOM.
2524
+ */ attach() {
2404
2525
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2405
2526
  const that = this;
2406
2527
  const widgetElement = this._options.viewElement;
@@ -2432,23 +2553,23 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2432
2553
  });
2433
2554
  }
2434
2555
  /**
2435
- * Starts the resizing process.
2436
- *
2437
- * Creates a new {@link #state} for the current process.
2438
- *
2439
- * @fires begin
2440
- * @param domResizeHandle Clicked handle.
2441
- */ begin(domResizeHandle) {
2556
+ * Starts the resizing process.
2557
+ *
2558
+ * Creates a new {@link #state} for the current process.
2559
+ *
2560
+ * @fires begin
2561
+ * @param domResizeHandle Clicked handle.
2562
+ */ begin(domResizeHandle) {
2442
2563
  this._state = new ResizeState(this._options);
2443
2564
  this._sizeView._bindToState(this._options, this.state);
2444
2565
  this._initialViewWidth = this._options.viewElement.getStyle('width');
2445
2566
  this.state.begin(domResizeHandle, this._getHandleHost(), this._getResizeHost());
2446
2567
  }
2447
2568
  /**
2448
- * Updates the proposed size based on `domEventData`.
2449
- *
2450
- * @fires updateSize
2451
- */ updateSize(domEventData) {
2569
+ * Updates the proposed size based on `domEventData`.
2570
+ *
2571
+ * @fires updateSize
2572
+ */ updateSize(domEventData) {
2452
2573
  const newSize = this._proposeNewSize(domEventData);
2453
2574
  const editingView = this._options.editor.editing.view;
2454
2575
  editingView.change((writer)=>{
@@ -2475,10 +2596,10 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2475
2596
  });
2476
2597
  }
2477
2598
  /**
2478
- * Applies the geometry proposed with the resizer.
2479
- *
2480
- * @fires commit
2481
- */ commit() {
2599
+ * Applies the geometry proposed with the resizer.
2600
+ *
2601
+ * @fires commit
2602
+ */ commit() {
2482
2603
  const unit = this._options.unit || '%';
2483
2604
  const newValue = (unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth) + unit;
2484
2605
  // Both cleanup and onCommit callback are very likely to make view changes. Ensure that it is made in a single step.
@@ -2488,22 +2609,22 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2488
2609
  });
2489
2610
  }
2490
2611
  /**
2491
- * Cancels and rejects the proposed resize dimensions, hiding the UI.
2492
- *
2493
- * @fires cancel
2494
- */ cancel() {
2612
+ * Cancels and rejects the proposed resize dimensions, hiding the UI.
2613
+ *
2614
+ * @fires cancel
2615
+ */ cancel() {
2495
2616
  this._cleanup();
2496
2617
  }
2497
2618
  /**
2498
- * Destroys the resizer.
2499
- */ destroy() {
2619
+ * Destroys the resizer.
2620
+ */ destroy() {
2500
2621
  this.cancel();
2501
2622
  }
2502
2623
  /**
2503
- * Redraws the resizer.
2504
- *
2505
- * @param handleHostRect Handle host rectangle might be given to improve performance.
2506
- */ redraw(handleHostRect) {
2624
+ * Redraws the resizer.
2625
+ *
2626
+ * @param handleHostRect Handle host rectangle might be given to improve performance.
2627
+ */ redraw(handleHostRect) {
2507
2628
  const domWrapper = this._domResizerWrapper;
2508
2629
  // Refresh only if resizer exists in the DOM.
2509
2630
  if (!existsInDom(domWrapper)) {
@@ -2558,8 +2679,8 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2558
2679
  return domElement.classList.contains('ck-widget__resizer__handle');
2559
2680
  }
2560
2681
  /**
2561
- * Cleans up the context state.
2562
- */ _cleanup() {
2682
+ * Cleans up the context state.
2683
+ */ _cleanup() {
2563
2684
  this._sizeView._dismiss();
2564
2685
  const editingView = this._options.editor.editing.view;
2565
2686
  editingView.change((writer)=>{
@@ -2567,10 +2688,10 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2567
2688
  });
2568
2689
  }
2569
2690
  /**
2570
- * Calculates the proposed size as the resize handles are dragged.
2571
- *
2572
- * @param domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
2573
- */ _proposeNewSize(domEventData) {
2691
+ * Calculates the proposed size as the resize handles are dragged.
2692
+ *
2693
+ * @param domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
2694
+ */ _proposeNewSize(domEventData) {
2574
2695
  const state = this.state;
2575
2696
  const currentCoordinates = extractCoordinates(domEventData);
2576
2697
  const isCentered = this._options.isCentered ? this._options.isCentered(this) : true;
@@ -2616,37 +2737,37 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2616
2737
  };
2617
2738
  }
2618
2739
  /**
2619
- * Obtains the resize host.
2620
- *
2621
- * Resize host is an object that receives dimensions which are the result of resizing.
2622
- */ _getResizeHost() {
2740
+ * Obtains the resize host.
2741
+ *
2742
+ * Resize host is an object that receives dimensions which are the result of resizing.
2743
+ */ _getResizeHost() {
2623
2744
  const widgetWrapper = this._domResizerWrapper.parentElement;
2624
2745
  return this._options.getResizeHost(widgetWrapper);
2625
2746
  }
2626
2747
  /**
2627
- * Obtains the handle host.
2628
- *
2629
- * Handle host is an object that the handles are aligned to.
2630
- *
2631
- * Handle host will not always be an entire widget itself. Take an image as an example. The image widget
2632
- * contains an image and a caption. Only the image should be surrounded with handles.
2633
- */ _getHandleHost() {
2748
+ * Obtains the handle host.
2749
+ *
2750
+ * Handle host is an object that the handles are aligned to.
2751
+ *
2752
+ * Handle host will not always be an entire widget itself. Take an image as an example. The image widget
2753
+ * contains an image and a caption. Only the image should be surrounded with handles.
2754
+ */ _getHandleHost() {
2634
2755
  const widgetWrapper = this._domResizerWrapper.parentElement;
2635
2756
  return this._options.getHandleHost(widgetWrapper);
2636
2757
  }
2637
2758
  /**
2638
- * DOM container of the entire resize UI.
2639
- *
2640
- * Note that this property will have a value only after the element bound with the resizer is rendered
2641
- * (otherwise `null`).
2642
- */ get _domResizerWrapper() {
2759
+ * DOM container of the entire resize UI.
2760
+ *
2761
+ * Note that this property will have a value only after the element bound with the resizer is rendered
2762
+ * (otherwise `null`).
2763
+ */ get _domResizerWrapper() {
2643
2764
  return this._options.editor.editing.view.domConverter.mapViewToDom(this._viewResizerWrapper);
2644
2765
  }
2645
2766
  /**
2646
- * Renders the resize handles in the DOM.
2647
- *
2648
- * @param domElement The resizer wrapper.
2649
- */ _appendHandles(domElement) {
2767
+ * Renders the resize handles in the DOM.
2768
+ *
2769
+ * @param domElement The resizer wrapper.
2770
+ */ _appendHandles(domElement) {
2650
2771
  const resizerPositions = [
2651
2772
  'top-left',
2652
2773
  'top-right',
@@ -2663,39 +2784,13 @@ class Resizer extends /* #__PURE__ */ ObservableMixin() {
2663
2784
  }
2664
2785
  }
2665
2786
  /**
2666
- * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
2667
- */ _appendSizeUI(domElement) {
2787
+ * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
2788
+ */ _appendSizeUI(domElement) {
2668
2789
  this._sizeView = new SizeView();
2669
2790
  // Make sure icon#element is rendered before passing to appendChild().
2670
2791
  this._sizeView.render();
2671
2792
  domElement.appendChild(this._sizeView.element);
2672
2793
  }
2673
- /**
2674
- * @param options Resizer options.
2675
- */ constructor(options){
2676
- super();
2677
- /**
2678
- * A wrapper that is controlled by the resizer. This is usually a widget element.
2679
- */ this._viewResizerWrapper = null;
2680
- this._options = options;
2681
- this.set('isEnabled', true);
2682
- this.set('isSelected', false);
2683
- this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected)=>isEnabled && isSelected);
2684
- this.decorate('begin');
2685
- this.decorate('cancel');
2686
- this.decorate('commit');
2687
- this.decorate('updateSize');
2688
- this.on('commit', (event)=>{
2689
- // State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
2690
- // cleaned up (#5195).
2691
- if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
2692
- this._cleanup();
2693
- event.stop();
2694
- }
2695
- }, {
2696
- priority: 'high'
2697
- });
2698
- }
2699
2794
  }
2700
2795
  /**
2701
2796
  * @param resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
@@ -2713,15 +2808,24 @@ function existsInDom(element) {
2713
2808
  return element && element.ownerDocument && element.ownerDocument.contains(element);
2714
2809
  }
2715
2810
 
2716
- class WidgetResize extends Plugin {
2811
+ /**
2812
+ * The widget resize feature plugin.
2813
+ *
2814
+ * Use the {@link module:widget/widgetresize~WidgetResize#attachTo} method to create a resizer for the specified widget.
2815
+ */ class WidgetResize extends Plugin {
2717
2816
  /**
2718
- * @inheritDoc
2719
- */ static get pluginName() {
2817
+ * A map of resizers created using this plugin instance.
2818
+ */ _resizers = new Map();
2819
+ _observer;
2820
+ _redrawSelectedResizerThrottled;
2821
+ /**
2822
+ * @inheritDoc
2823
+ */ static get pluginName() {
2720
2824
  return 'WidgetResize';
2721
2825
  }
2722
2826
  /**
2723
- * @inheritDoc
2724
- */ init() {
2827
+ * @inheritDoc
2828
+ */ init() {
2725
2829
  const editing = this.editor.editing;
2726
2830
  const domDocument = global.window.document;
2727
2831
  this.set('selectedResizer', null);
@@ -2763,15 +2867,15 @@ class WidgetResize extends Plugin {
2763
2867
  });
2764
2868
  }
2765
2869
  /**
2766
- * Redraws the selected resizer if there is any selected resizer and if it is visible.
2767
- */ redrawSelectedResizer() {
2870
+ * Redraws the selected resizer if there is any selected resizer and if it is visible.
2871
+ */ redrawSelectedResizer() {
2768
2872
  if (this.selectedResizer && this.selectedResizer.isVisible) {
2769
2873
  this.selectedResizer.redraw();
2770
2874
  }
2771
2875
  }
2772
2876
  /**
2773
- * @inheritDoc
2774
- */ destroy() {
2877
+ * @inheritDoc
2878
+ */ destroy() {
2775
2879
  super.destroy();
2776
2880
  this._observer.stopListening();
2777
2881
  for (const resizer of this._resizers.values()){
@@ -2780,23 +2884,23 @@ class WidgetResize extends Plugin {
2780
2884
  this._redrawSelectedResizerThrottled.cancel();
2781
2885
  }
2782
2886
  /**
2783
- * Marks resizer as selected.
2784
- */ select(resizer) {
2887
+ * Marks resizer as selected.
2888
+ */ select(resizer) {
2785
2889
  this.deselect();
2786
2890
  this.selectedResizer = resizer;
2787
2891
  this.selectedResizer.isSelected = true;
2788
2892
  }
2789
2893
  /**
2790
- * Deselects currently set resizer.
2791
- */ deselect() {
2894
+ * Deselects currently set resizer.
2895
+ */ deselect() {
2792
2896
  if (this.selectedResizer) {
2793
2897
  this.selectedResizer.isSelected = false;
2794
2898
  }
2795
2899
  this.selectedResizer = null;
2796
2900
  }
2797
2901
  /**
2798
- * @param options Resizer options.
2799
- */ attachTo(options) {
2902
+ * @param options Resizer options.
2903
+ */ attachTo(options) {
2800
2904
  const resizer = new Resizer(options);
2801
2905
  const plugins = this.editor.plugins;
2802
2906
  resizer.attach();
@@ -2830,15 +2934,15 @@ class WidgetResize extends Plugin {
2830
2934
  return resizer;
2831
2935
  }
2832
2936
  /**
2833
- * Returns a resizer created for a given view element (widget element).
2834
- *
2835
- * @param viewElement View element associated with the resizer.
2836
- */ getResizerByViewElement(viewElement) {
2937
+ * Returns a resizer created for a given view element (widget element).
2938
+ *
2939
+ * @param viewElement View element associated with the resizer.
2940
+ */ getResizerByViewElement(viewElement) {
2837
2941
  return this._resizers.get(viewElement);
2838
2942
  }
2839
2943
  /**
2840
- * Returns a resizer that contains a given resize handle.
2841
- */ _getResizerByHandle(domResizeHandle) {
2944
+ * Returns a resizer that contains a given resize handle.
2945
+ */ _getResizerByHandle(domResizeHandle) {
2842
2946
  for (const resizer of this._resizers.values()){
2843
2947
  if (resizer.containsHandle(domResizeHandle)) {
2844
2948
  return resizer;
@@ -2846,8 +2950,8 @@ class WidgetResize extends Plugin {
2846
2950
  }
2847
2951
  }
2848
2952
  /**
2849
- * @param domEventData Native DOM event.
2850
- */ _mouseDownListener(event, domEventData) {
2953
+ * @param domEventData Native DOM event.
2954
+ */ _mouseDownListener(event, domEventData) {
2851
2955
  const resizeHandle = domEventData.domTarget;
2852
2956
  if (!Resizer.isResizeHandle(resizeHandle)) {
2853
2957
  return;
@@ -2861,8 +2965,8 @@ class WidgetResize extends Plugin {
2861
2965
  }
2862
2966
  }
2863
2967
  /**
2864
- * @param domEventData Native DOM event.
2865
- */ _mouseMoveListener(event, domEventData) {
2968
+ * @param domEventData Native DOM event.
2969
+ */ _mouseMoveListener(event, domEventData) {
2866
2970
  if (this._activeResizer) {
2867
2971
  this._activeResizer.updateSize(domEventData);
2868
2972
  }
@@ -2873,12 +2977,6 @@ class WidgetResize extends Plugin {
2873
2977
  this._activeResizer = null;
2874
2978
  }
2875
2979
  }
2876
- constructor(){
2877
- super(...arguments);
2878
- /**
2879
- * A map of resizers created using this plugin instance.
2880
- */ this._resizers = new Map();
2881
- }
2882
2980
  }
2883
2981
 
2884
2982
  export { WIDGET_CLASS_NAME, WIDGET_SELECTED_CLASS_NAME, Widget, WidgetResize, WidgetToolbarRepository, WidgetTypeAround, calculateResizeHostAncestorWidth, calculateResizeHostPercentageWidth, findOptimalInsertionRange, getLabel, isWidget, setHighlightHandling, setLabel, toWidget, toWidgetEditable, viewToModelPositionOutsideModelElement };