@ckeditor/ckeditor5-ui 38.0.1 → 38.1.1

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.
Files changed (127) hide show
  1. package/lang/translations/it.po +1 -1
  2. package/package.json +3 -32
  3. package/src/augmentation.d.ts +86 -86
  4. package/src/augmentation.js +5 -5
  5. package/src/bindings/addkeyboardhandlingforgrid.d.ts +27 -27
  6. package/src/bindings/addkeyboardhandlingforgrid.js +107 -107
  7. package/src/bindings/clickoutsidehandler.d.ts +28 -28
  8. package/src/bindings/clickoutsidehandler.js +36 -36
  9. package/src/bindings/csstransitiondisablermixin.d.ts +40 -40
  10. package/src/bindings/csstransitiondisablermixin.js +55 -55
  11. package/src/bindings/injectcsstransitiondisabler.d.ts +59 -59
  12. package/src/bindings/injectcsstransitiondisabler.js +71 -71
  13. package/src/bindings/preventdefault.d.ts +33 -33
  14. package/src/bindings/preventdefault.js +34 -34
  15. package/src/bindings/submithandler.d.ts +57 -57
  16. package/src/bindings/submithandler.js +47 -47
  17. package/src/button/button.d.ts +178 -178
  18. package/src/button/button.js +5 -5
  19. package/src/button/buttonview.d.ts +177 -177
  20. package/src/button/buttonview.js +231 -231
  21. package/src/button/switchbuttonview.d.ts +45 -45
  22. package/src/button/switchbuttonview.js +75 -75
  23. package/src/colorgrid/colorgridview.d.ts +132 -132
  24. package/src/colorgrid/colorgridview.js +124 -124
  25. package/src/colorgrid/colortileview.d.ts +28 -28
  26. package/src/colorgrid/colortileview.js +40 -40
  27. package/src/colorgrid/utils.d.ts +47 -47
  28. package/src/colorgrid/utils.js +84 -84
  29. package/src/colorpicker/colorpickerview.d.ts +110 -110
  30. package/src/colorpicker/colorpickerview.js +253 -250
  31. package/src/colorpicker/utils.d.ts +35 -35
  32. package/src/colorpicker/utils.js +99 -99
  33. package/src/componentfactory.d.ts +81 -81
  34. package/src/componentfactory.js +104 -104
  35. package/src/dropdown/button/dropdownbutton.d.ts +25 -25
  36. package/src/dropdown/button/dropdownbutton.js +5 -5
  37. package/src/dropdown/button/dropdownbuttonview.d.ts +48 -48
  38. package/src/dropdown/button/dropdownbuttonview.js +66 -66
  39. package/src/dropdown/button/splitbuttonview.d.ts +161 -161
  40. package/src/dropdown/button/splitbuttonview.js +152 -152
  41. package/src/dropdown/dropdownpanelfocusable.d.ts +21 -21
  42. package/src/dropdown/dropdownpanelfocusable.js +5 -5
  43. package/src/dropdown/dropdownpanelview.d.ts +62 -62
  44. package/src/dropdown/dropdownpanelview.js +96 -96
  45. package/src/dropdown/dropdownview.d.ts +315 -315
  46. package/src/dropdown/dropdownview.js +378 -378
  47. package/src/dropdown/utils.d.ts +221 -221
  48. package/src/dropdown/utils.js +434 -434
  49. package/src/editableui/editableuiview.d.ts +72 -72
  50. package/src/editableui/editableuiview.js +112 -112
  51. package/src/editableui/inline/inlineeditableuiview.d.ts +40 -40
  52. package/src/editableui/inline/inlineeditableuiview.js +48 -48
  53. package/src/editorui/bodycollection.d.ts +55 -51
  54. package/src/editorui/bodycollection.js +84 -78
  55. package/src/editorui/boxed/boxededitoruiview.d.ts +40 -40
  56. package/src/editorui/boxed/boxededitoruiview.js +81 -81
  57. package/src/editorui/editorui.d.ts +282 -274
  58. package/src/editorui/editorui.js +410 -388
  59. package/src/editorui/editoruiview.d.ts +39 -39
  60. package/src/editorui/editoruiview.js +38 -38
  61. package/src/editorui/poweredby.d.ts +71 -76
  62. package/src/editorui/poweredby.js +294 -291
  63. package/src/focuscycler.d.ts +183 -183
  64. package/src/focuscycler.js +220 -220
  65. package/src/formheader/formheaderview.d.ts +53 -53
  66. package/src/formheader/formheaderview.js +63 -63
  67. package/src/icon/iconview.d.ts +78 -78
  68. package/src/icon/iconview.js +112 -112
  69. package/src/iframe/iframeview.d.ts +50 -50
  70. package/src/iframe/iframeview.js +63 -63
  71. package/src/index.d.ts +62 -61
  72. package/src/index.js +61 -60
  73. package/src/input/inputview.d.ts +121 -121
  74. package/src/input/inputview.js +106 -106
  75. package/src/inputnumber/inputnumberview.d.ts +49 -49
  76. package/src/inputnumber/inputnumberview.js +40 -40
  77. package/src/inputtext/inputtextview.d.ts +18 -18
  78. package/src/inputtext/inputtextview.js +27 -27
  79. package/src/label/labelview.d.ts +36 -36
  80. package/src/label/labelview.js +41 -41
  81. package/src/labeledfield/labeledfieldview.d.ts +182 -182
  82. package/src/labeledfield/labeledfieldview.js +157 -157
  83. package/src/labeledfield/utils.d.ts +93 -93
  84. package/src/labeledfield/utils.js +131 -131
  85. package/src/labeledinput/labeledinputview.d.ts +125 -125
  86. package/src/labeledinput/labeledinputview.js +125 -125
  87. package/src/list/listitemview.d.ts +35 -35
  88. package/src/list/listitemview.js +40 -40
  89. package/src/list/listseparatorview.d.ts +18 -18
  90. package/src/list/listseparatorview.js +28 -28
  91. package/src/list/listview.d.ts +65 -65
  92. package/src/list/listview.js +90 -90
  93. package/src/model.d.ts +22 -22
  94. package/src/model.js +31 -31
  95. package/src/notification/notification.d.ts +211 -211
  96. package/src/notification/notification.js +187 -187
  97. package/src/panel/balloon/balloonpanelview.d.ts +685 -685
  98. package/src/panel/balloon/balloonpanelview.js +988 -988
  99. package/src/panel/balloon/contextualballoon.d.ts +299 -299
  100. package/src/panel/balloon/contextualballoon.js +572 -572
  101. package/src/panel/sticky/stickypanelview.d.ts +132 -132
  102. package/src/panel/sticky/stickypanelview.js +139 -139
  103. package/src/template.d.ts +942 -942
  104. package/src/template.js +1294 -1294
  105. package/src/toolbar/balloon/balloontoolbar.d.ts +122 -122
  106. package/src/toolbar/balloon/balloontoolbar.js +300 -300
  107. package/src/toolbar/block/blockbuttonview.d.ts +35 -35
  108. package/src/toolbar/block/blockbuttonview.js +41 -41
  109. package/src/toolbar/block/blocktoolbar.d.ts +161 -153
  110. package/src/toolbar/block/blocktoolbar.js +391 -374
  111. package/src/toolbar/normalizetoolbarconfig.d.ts +39 -39
  112. package/src/toolbar/normalizetoolbarconfig.js +51 -51
  113. package/src/toolbar/toolbarlinebreakview.d.ts +18 -18
  114. package/src/toolbar/toolbarlinebreakview.js +28 -28
  115. package/src/toolbar/toolbarseparatorview.d.ts +18 -18
  116. package/src/toolbar/toolbarseparatorview.js +28 -28
  117. package/src/toolbar/toolbarview.d.ts +265 -265
  118. package/src/toolbar/toolbarview.js +717 -717
  119. package/src/tooltipmanager.d.ts +180 -180
  120. package/src/tooltipmanager.js +353 -353
  121. package/src/view.d.ts +422 -422
  122. package/src/view.js +396 -396
  123. package/src/viewcollection.d.ts +139 -139
  124. package/src/viewcollection.js +206 -206
  125. package/theme/components/colorpicker/colorpicker.css +4 -4
  126. package/theme/globals/_poweredby.css +5 -1
  127. package/theme/icons/project-logo.svg +1 -1
@@ -1,291 +1,294 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { Rect, DomEmitterMixin, findClosestScrollableAncestor, verifyLicense } from '@ckeditor/ckeditor5-utils';
6
- import BalloonPanelView from '../panel/balloon/balloonpanelview';
7
- import IconView from '../icon/iconview';
8
- import View from '../view';
9
- import { throttle } from 'lodash-es';
10
- import poweredByIcon from '../../theme/icons/project-logo.svg';
11
- const ICON_WIDTH = 53;
12
- const ICON_HEIGHT = 10;
13
- const NARROW_ROOT_HEIGHT_THRESHOLD = 50;
14
- const NARROW_ROOT_WIDTH_THRESHOLD = 350;
15
- const DEFAULT_LABEL = 'Powered by';
16
- const OFF_THE_SCREEN_POSITION = {
17
- top: -99999,
18
- left: -99999,
19
- name: 'invalid',
20
- config: {
21
- withArrow: false
22
- }
23
- };
24
- /**
25
- * A helper that enables the "powered by" feature in the editor and renders a link to the project's
26
- * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
27
- *
28
- * @private
29
- */
30
- export default class PoweredBy extends DomEmitterMixin() {
31
- /**
32
- * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
33
- * event.
34
- *
35
- * @param editor
36
- */
37
- constructor(editor) {
38
- super();
39
- this.editor = editor;
40
- this._balloonView = null;
41
- this._lastFocusedEditableElement = null;
42
- this._showBalloonThrottled = throttle(this._showBalloon.bind(this), 50, { leading: true });
43
- editor.on('ready', this._handleEditorReady.bind(this));
44
- }
45
- /**
46
- * Destroys the "powered by" helper along with its view.
47
- */
48
- destroy() {
49
- const balloon = this._balloonView;
50
- if (balloon) {
51
- // Balloon gets destroyed by the body collection.
52
- // The powered by view gets destroyed by the balloon.
53
- balloon.unpin();
54
- this._balloonView = null;
55
- }
56
- this._showBalloonThrottled.cancel();
57
- this.stopListening();
58
- }
59
- /**
60
- * Enables "powered by" label once the editor (ui) is ready.
61
- */
62
- _handleEditorReady() {
63
- const editor = this.editor;
64
- /* istanbul ignore next -- @preserve */
65
- if (verifyLicense(editor.config.get('licenseKey')) === 'VALID') {
66
- return;
67
- }
68
- // No view means no body collection to append the powered by balloon to.
69
- if (!editor.ui.view) {
70
- return;
71
- }
72
- editor.ui.focusTracker.on('change:isFocused', (evt, data, isFocused) => {
73
- this._updateLastFocusedEditableElement();
74
- if (isFocused) {
75
- this._showBalloon();
76
- }
77
- else {
78
- this._hideBalloon();
79
- }
80
- });
81
- editor.ui.focusTracker.on('change:focusedElement', (evt, data, focusedElement) => {
82
- this._updateLastFocusedEditableElement();
83
- if (focusedElement) {
84
- this._showBalloon();
85
- }
86
- });
87
- editor.ui.on('update', () => {
88
- this._showBalloonThrottled();
89
- });
90
- }
91
- /**
92
- * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
93
- * with the "powered by" view inside ready for positioning.
94
- */
95
- _createBalloonView() {
96
- const editor = this.editor;
97
- const balloon = this._balloonView = new BalloonPanelView();
98
- const poweredByConfig = getNormalizedConfig(editor);
99
- const view = new PoweredByView(editor.locale, poweredByConfig.label);
100
- balloon.content.add(view);
101
- balloon.set({
102
- class: 'ck-powered-by-balloon'
103
- });
104
- editor.ui.view.body.add(balloon);
105
- editor.ui.focusTracker.add(balloon.element);
106
- this._balloonView = balloon;
107
- }
108
- /**
109
- * Attempts to display the balloon with the "powered by" view.
110
- */
111
- _showBalloon() {
112
- if (!this._lastFocusedEditableElement) {
113
- return;
114
- }
115
- const attachOptions = getBalloonAttachOptions(this.editor, this._lastFocusedEditableElement);
116
- if (attachOptions) {
117
- if (!this._balloonView) {
118
- this._createBalloonView();
119
- }
120
- this._balloonView.pin(attachOptions);
121
- }
122
- }
123
- /**
124
- * Hides the "powered by" balloon if already visible.
125
- */
126
- _hideBalloon() {
127
- if (this._balloonView) {
128
- this._balloonView.unpin();
129
- }
130
- }
131
- /**
132
- * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
133
- */
134
- _updateLastFocusedEditableElement() {
135
- const editor = this.editor;
136
- const isFocused = editor.ui.focusTracker.isFocused;
137
- const focusedElement = editor.ui.focusTracker.focusedElement;
138
- if (!isFocused || !focusedElement) {
139
- this._lastFocusedEditableElement = null;
140
- return;
141
- }
142
- const editableEditorElements = Array.from(editor.ui.getEditableElementsNames()).map(name => {
143
- return editor.ui.getEditableElement(name);
144
- });
145
- if (editableEditorElements.includes(focusedElement)) {
146
- this._lastFocusedEditableElement = focusedElement;
147
- }
148
- else {
149
- // If it's none of the editable element, then the focus is somewhere in the UI. Let's display powered by
150
- // over the first element then.
151
- this._lastFocusedEditableElement = editableEditorElements[0];
152
- }
153
- }
154
- }
155
- /**
156
- * A view displaying a "powered by" label and project logo wrapped in a link.
157
- */
158
- class PoweredByView extends View {
159
- /**
160
- * Created an instance of the "powered by" view.
161
- *
162
- * @param locale The localization services instance.
163
- * @param label The label text.
164
- */
165
- constructor(locale, label) {
166
- super(locale);
167
- const iconView = new IconView();
168
- const bind = this.bindTemplate;
169
- iconView.set({
170
- content: poweredByIcon,
171
- isColorInherited: false
172
- });
173
- iconView.extendTemplate({
174
- attributes: {
175
- style: {
176
- width: ICON_WIDTH + 'px',
177
- height: ICON_HEIGHT + 'px'
178
- }
179
- }
180
- });
181
- this.setTemplate({
182
- tag: 'div',
183
- attributes: {
184
- class: ['ck', 'ck-powered-by'],
185
- 'aria-hidden': true
186
- },
187
- children: [
188
- {
189
- tag: 'a',
190
- attributes: {
191
- href: 'https://ckeditor.com/?utm_source=ckeditor&' +
192
- 'utm_medium=referral&utm_campaign=701Dn000000hVgmIAE_powered_by_ckeditor_logo',
193
- target: '_blank',
194
- tabindex: '-1'
195
- },
196
- children: [
197
- ...label ? [
198
- {
199
- tag: 'span',
200
- attributes: {
201
- class: ['ck', 'ck-powered-by__label']
202
- },
203
- children: [label]
204
- }
205
- ] : [],
206
- iconView
207
- ],
208
- on: {
209
- dragstart: bind.to(evt => evt.preventDefault())
210
- }
211
- }
212
- ]
213
- });
214
- }
215
- }
216
- function getBalloonAttachOptions(editor, focusedEditableElement) {
217
- const poweredByConfig = getNormalizedConfig(editor);
218
- const positioningFunction = poweredByConfig.side === 'right' ?
219
- getLowerRightCornerPosition(focusedEditableElement, poweredByConfig) :
220
- getLowerLeftCornerPosition(focusedEditableElement, poweredByConfig);
221
- return {
222
- target: focusedEditableElement,
223
- positions: [positioningFunction]
224
- };
225
- }
226
- function getLowerRightCornerPosition(focusedEditableElement, config) {
227
- return getLowerCornerPosition(focusedEditableElement, config, (rootRect, balloonRect) => {
228
- return rootRect.left + rootRect.width - balloonRect.width - config.horizontalOffset;
229
- });
230
- }
231
- function getLowerLeftCornerPosition(focusedEditableElement, config) {
232
- return getLowerCornerPosition(focusedEditableElement, config, rootRect => rootRect.left + config.horizontalOffset);
233
- }
234
- function getLowerCornerPosition(focusedEditableElement, config, getBalloonLeft) {
235
- return (editableElementRect, balloonRect) => {
236
- const visibleEditableElementRect = editableElementRect.getVisible();
237
- // Root cropped by ancestors.
238
- if (!visibleEditableElementRect) {
239
- return OFF_THE_SCREEN_POSITION;
240
- }
241
- if (editableElementRect.width < NARROW_ROOT_WIDTH_THRESHOLD || editableElementRect.height < NARROW_ROOT_HEIGHT_THRESHOLD) {
242
- return OFF_THE_SCREEN_POSITION;
243
- }
244
- let balloonTop;
245
- if (config.position === 'inside') {
246
- balloonTop = editableElementRect.bottom - balloonRect.height;
247
- }
248
- else {
249
- balloonTop = editableElementRect.bottom - balloonRect.height / 2;
250
- }
251
- balloonTop -= config.verticalOffset;
252
- const balloonLeft = getBalloonLeft(editableElementRect, balloonRect);
253
- if (config.position === 'inside') {
254
- const newBalloonRect = balloonRect.clone().moveTo(balloonLeft, balloonTop);
255
- // The watermark cannot be positioned in this corner because the corner is not quite visible.
256
- if (newBalloonRect.getIntersectionArea(visibleEditableElementRect) < newBalloonRect.getArea()) {
257
- return OFF_THE_SCREEN_POSITION;
258
- }
259
- }
260
- else {
261
- const firstScrollableEditableElementAncestor = findClosestScrollableAncestor(focusedEditableElement);
262
- if (firstScrollableEditableElementAncestor) {
263
- const firstScrollableEditableElementAncestorRect = new Rect(firstScrollableEditableElementAncestor);
264
- // The watermark cannot be positioned in this corner because the corner is "not visible enough".
265
- if (visibleEditableElementRect.bottom + balloonRect.height / 2 > firstScrollableEditableElementAncestorRect.bottom) {
266
- return OFF_THE_SCREEN_POSITION;
267
- }
268
- }
269
- }
270
- return {
271
- top: balloonTop,
272
- left: balloonLeft,
273
- name: `position_${config.position}-side_${config.side}`,
274
- config: {
275
- withArrow: false
276
- }
277
- };
278
- };
279
- }
280
- function getNormalizedConfig(editor) {
281
- const userConfig = editor.config.get('ui.poweredBy');
282
- const position = userConfig && userConfig.position || 'border';
283
- return {
284
- position,
285
- label: DEFAULT_LABEL,
286
- verticalOffset: position === 'inside' ? 5 : 0,
287
- horizontalOffset: 5,
288
- side: editor.locale.contentLanguageDirection === 'ltr' ? 'right' : 'left',
289
- ...userConfig
290
- };
291
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { Rect, DomEmitterMixin, findClosestScrollableAncestor, verifyLicense } from '@ckeditor/ckeditor5-utils';
6
+ import BalloonPanelView from '../panel/balloon/balloonpanelview';
7
+ import IconView from '../icon/iconview';
8
+ import View from '../view';
9
+ import { throttle } from 'lodash-es';
10
+ import poweredByIcon from '../../theme/icons/project-logo.svg';
11
+ const ICON_WIDTH = 53;
12
+ const ICON_HEIGHT = 10;
13
+ // Note, whenever changing the threshold, make sure to update the docs/support/managing-ckeditor-logo.md docs
14
+ // as this information is also mentioned there ⚠.
15
+ const NARROW_ROOT_HEIGHT_THRESHOLD = 50;
16
+ const NARROW_ROOT_WIDTH_THRESHOLD = 350;
17
+ const DEFAULT_LABEL = 'Powered by';
18
+ const OFF_THE_SCREEN_POSITION = {
19
+ top: -99999,
20
+ left: -99999,
21
+ name: 'invalid',
22
+ config: {
23
+ withArrow: false
24
+ }
25
+ };
26
+ /**
27
+ * A helper that enables the "powered by" feature in the editor and renders a link to the project's
28
+ * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
29
+ *
30
+ * @private
31
+ */
32
+ export default class PoweredBy extends DomEmitterMixin() {
33
+ /**
34
+ * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
35
+ * event.
36
+ *
37
+ * @param editor
38
+ */
39
+ constructor(editor) {
40
+ super();
41
+ this.editor = editor;
42
+ this._balloonView = null;
43
+ this._lastFocusedEditableElement = null;
44
+ this._showBalloonThrottled = throttle(this._showBalloon.bind(this), 50, { leading: true });
45
+ editor.on('ready', this._handleEditorReady.bind(this));
46
+ }
47
+ /**
48
+ * Destroys the "powered by" helper along with its view.
49
+ */
50
+ destroy() {
51
+ const balloon = this._balloonView;
52
+ if (balloon) {
53
+ // Balloon gets destroyed by the body collection.
54
+ // The powered by view gets destroyed by the balloon.
55
+ balloon.unpin();
56
+ this._balloonView = null;
57
+ }
58
+ this._showBalloonThrottled.cancel();
59
+ this.stopListening();
60
+ }
61
+ /**
62
+ * Enables "powered by" label once the editor (ui) is ready.
63
+ */
64
+ _handleEditorReady() {
65
+ const editor = this.editor;
66
+ const forceVisible = !!editor.config.get('ui.poweredBy.forceVisible');
67
+ /* istanbul ignore next -- @preserve */
68
+ if (!forceVisible && verifyLicense(editor.config.get('licenseKey')) === 'VALID') {
69
+ return;
70
+ }
71
+ // No view means no body collection to append the powered by balloon to.
72
+ if (!editor.ui.view) {
73
+ return;
74
+ }
75
+ editor.ui.focusTracker.on('change:isFocused', (evt, data, isFocused) => {
76
+ this._updateLastFocusedEditableElement();
77
+ if (isFocused) {
78
+ this._showBalloon();
79
+ }
80
+ else {
81
+ this._hideBalloon();
82
+ }
83
+ });
84
+ editor.ui.focusTracker.on('change:focusedElement', (evt, data, focusedElement) => {
85
+ this._updateLastFocusedEditableElement();
86
+ if (focusedElement) {
87
+ this._showBalloon();
88
+ }
89
+ });
90
+ editor.ui.on('update', () => {
91
+ this._showBalloonThrottled();
92
+ });
93
+ }
94
+ /**
95
+ * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
96
+ * with the "powered by" view inside ready for positioning.
97
+ */
98
+ _createBalloonView() {
99
+ const editor = this.editor;
100
+ const balloon = this._balloonView = new BalloonPanelView();
101
+ const poweredByConfig = getNormalizedConfig(editor);
102
+ const view = new PoweredByView(editor.locale, poweredByConfig.label);
103
+ balloon.content.add(view);
104
+ balloon.set({
105
+ class: 'ck-powered-by-balloon'
106
+ });
107
+ editor.ui.view.body.add(balloon);
108
+ editor.ui.focusTracker.add(balloon.element);
109
+ this._balloonView = balloon;
110
+ }
111
+ /**
112
+ * Attempts to display the balloon with the "powered by" view.
113
+ */
114
+ _showBalloon() {
115
+ if (!this._lastFocusedEditableElement) {
116
+ return;
117
+ }
118
+ const attachOptions = getBalloonAttachOptions(this.editor, this._lastFocusedEditableElement);
119
+ if (attachOptions) {
120
+ if (!this._balloonView) {
121
+ this._createBalloonView();
122
+ }
123
+ this._balloonView.pin(attachOptions);
124
+ }
125
+ }
126
+ /**
127
+ * Hides the "powered by" balloon if already visible.
128
+ */
129
+ _hideBalloon() {
130
+ if (this._balloonView) {
131
+ this._balloonView.unpin();
132
+ }
133
+ }
134
+ /**
135
+ * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
136
+ */
137
+ _updateLastFocusedEditableElement() {
138
+ const editor = this.editor;
139
+ const isFocused = editor.ui.focusTracker.isFocused;
140
+ const focusedElement = editor.ui.focusTracker.focusedElement;
141
+ if (!isFocused || !focusedElement) {
142
+ this._lastFocusedEditableElement = null;
143
+ return;
144
+ }
145
+ const editableEditorElements = Array.from(editor.ui.getEditableElementsNames()).map(name => {
146
+ return editor.ui.getEditableElement(name);
147
+ });
148
+ if (editableEditorElements.includes(focusedElement)) {
149
+ this._lastFocusedEditableElement = focusedElement;
150
+ }
151
+ else {
152
+ // If it's none of the editable element, then the focus is somewhere in the UI. Let's display powered by
153
+ // over the first element then.
154
+ this._lastFocusedEditableElement = editableEditorElements[0];
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * A view displaying a "powered by" label and project logo wrapped in a link.
160
+ */
161
+ class PoweredByView extends View {
162
+ /**
163
+ * Created an instance of the "powered by" view.
164
+ *
165
+ * @param locale The localization services instance.
166
+ * @param label The label text.
167
+ */
168
+ constructor(locale, label) {
169
+ super(locale);
170
+ const iconView = new IconView();
171
+ const bind = this.bindTemplate;
172
+ iconView.set({
173
+ content: poweredByIcon,
174
+ isColorInherited: false
175
+ });
176
+ iconView.extendTemplate({
177
+ attributes: {
178
+ style: {
179
+ width: ICON_WIDTH + 'px',
180
+ height: ICON_HEIGHT + 'px'
181
+ }
182
+ }
183
+ });
184
+ this.setTemplate({
185
+ tag: 'div',
186
+ attributes: {
187
+ class: ['ck', 'ck-powered-by'],
188
+ 'aria-hidden': true
189
+ },
190
+ children: [
191
+ {
192
+ tag: 'a',
193
+ attributes: {
194
+ href: 'https://ckeditor.com/?utm_source=ckeditor&' +
195
+ 'utm_medium=referral&utm_campaign=701Dn000000hVgmIAE_powered_by_ckeditor_logo',
196
+ target: '_blank',
197
+ tabindex: '-1'
198
+ },
199
+ children: [
200
+ ...label ? [
201
+ {
202
+ tag: 'span',
203
+ attributes: {
204
+ class: ['ck', 'ck-powered-by__label']
205
+ },
206
+ children: [label]
207
+ }
208
+ ] : [],
209
+ iconView
210
+ ],
211
+ on: {
212
+ dragstart: bind.to(evt => evt.preventDefault())
213
+ }
214
+ }
215
+ ]
216
+ });
217
+ }
218
+ }
219
+ function getBalloonAttachOptions(editor, focusedEditableElement) {
220
+ const poweredByConfig = getNormalizedConfig(editor);
221
+ const positioningFunction = poweredByConfig.side === 'right' ?
222
+ getLowerRightCornerPosition(focusedEditableElement, poweredByConfig) :
223
+ getLowerLeftCornerPosition(focusedEditableElement, poweredByConfig);
224
+ return {
225
+ target: focusedEditableElement,
226
+ positions: [positioningFunction]
227
+ };
228
+ }
229
+ function getLowerRightCornerPosition(focusedEditableElement, config) {
230
+ return getLowerCornerPosition(focusedEditableElement, config, (rootRect, balloonRect) => {
231
+ return rootRect.left + rootRect.width - balloonRect.width - config.horizontalOffset;
232
+ });
233
+ }
234
+ function getLowerLeftCornerPosition(focusedEditableElement, config) {
235
+ return getLowerCornerPosition(focusedEditableElement, config, rootRect => rootRect.left + config.horizontalOffset);
236
+ }
237
+ function getLowerCornerPosition(focusedEditableElement, config, getBalloonLeft) {
238
+ return (editableElementRect, balloonRect) => {
239
+ const visibleEditableElementRect = editableElementRect.getVisible();
240
+ // Root cropped by ancestors.
241
+ if (!visibleEditableElementRect) {
242
+ return OFF_THE_SCREEN_POSITION;
243
+ }
244
+ if (editableElementRect.width < NARROW_ROOT_WIDTH_THRESHOLD || editableElementRect.height < NARROW_ROOT_HEIGHT_THRESHOLD) {
245
+ return OFF_THE_SCREEN_POSITION;
246
+ }
247
+ let balloonTop;
248
+ if (config.position === 'inside') {
249
+ balloonTop = editableElementRect.bottom - balloonRect.height;
250
+ }
251
+ else {
252
+ balloonTop = editableElementRect.bottom - balloonRect.height / 2;
253
+ }
254
+ balloonTop -= config.verticalOffset;
255
+ const balloonLeft = getBalloonLeft(editableElementRect, balloonRect);
256
+ if (config.position === 'inside') {
257
+ const newBalloonRect = balloonRect.clone().moveTo(balloonLeft, balloonTop);
258
+ // The watermark cannot be positioned in this corner because the corner is not quite visible.
259
+ if (newBalloonRect.getIntersectionArea(visibleEditableElementRect) < newBalloonRect.getArea()) {
260
+ return OFF_THE_SCREEN_POSITION;
261
+ }
262
+ }
263
+ else {
264
+ const firstScrollableEditableElementAncestor = findClosestScrollableAncestor(focusedEditableElement);
265
+ if (firstScrollableEditableElementAncestor) {
266
+ const firstScrollableEditableElementAncestorRect = new Rect(firstScrollableEditableElementAncestor);
267
+ // The watermark cannot be positioned in this corner because the corner is "not visible enough".
268
+ if (visibleEditableElementRect.bottom + balloonRect.height / 2 > firstScrollableEditableElementAncestorRect.bottom) {
269
+ return OFF_THE_SCREEN_POSITION;
270
+ }
271
+ }
272
+ }
273
+ return {
274
+ top: balloonTop,
275
+ left: balloonLeft,
276
+ name: `position_${config.position}-side_${config.side}`,
277
+ config: {
278
+ withArrow: false
279
+ }
280
+ };
281
+ };
282
+ }
283
+ function getNormalizedConfig(editor) {
284
+ const userConfig = editor.config.get('ui.poweredBy');
285
+ const position = userConfig && userConfig.position || 'border';
286
+ return {
287
+ position,
288
+ label: DEFAULT_LABEL,
289
+ verticalOffset: position === 'inside' ? 5 : 0,
290
+ horizontalOffset: 5,
291
+ side: editor.locale.contentLanguageDirection === 'ltr' ? 'right' : 'left',
292
+ ...userConfig
293
+ };
294
+ }