suneditor 3.0.0-beta.2 → 3.0.0-beta.20

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 (177) hide show
  1. package/CONTRIBUTING.md +186 -184
  2. package/LICENSE +21 -21
  3. package/README.md +157 -180
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +126 -123
  7. package/src/assets/design/color.css +131 -121
  8. package/src/assets/design/index.css +3 -3
  9. package/src/assets/design/size.css +37 -35
  10. package/src/assets/design/typography.css +37 -37
  11. package/src/assets/icons/defaultIcons.js +247 -232
  12. package/src/assets/suneditor-contents.css +779 -778
  13. package/src/assets/suneditor.css +43 -35
  14. package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
  19. package/src/core/base/eventManager.js +1550 -1484
  20. package/src/core/base/history.js +355 -355
  21. package/src/core/class/char.js +163 -162
  22. package/src/core/class/component.js +856 -842
  23. package/src/core/class/format.js +3433 -3422
  24. package/src/core/class/html.js +1927 -1890
  25. package/src/core/class/menu.js +357 -346
  26. package/src/core/class/nodeTransform.js +424 -424
  27. package/src/core/class/offset.js +858 -891
  28. package/src/core/class/selection.js +710 -620
  29. package/src/core/class/shortcuts.js +98 -98
  30. package/src/core/class/toolbar.js +438 -430
  31. package/src/core/class/ui.js +424 -422
  32. package/src/core/class/viewer.js +750 -750
  33. package/src/core/editor.js +1810 -1708
  34. package/src/core/section/actives.js +268 -241
  35. package/src/core/section/constructor.js +1348 -1661
  36. package/src/core/section/context.js +102 -102
  37. package/src/core/section/documentType.js +582 -561
  38. package/src/core/section/options.js +367 -0
  39. package/src/core/util/instanceCheck.js +59 -0
  40. package/src/editorInjector/_classes.js +36 -36
  41. package/src/editorInjector/_core.js +92 -92
  42. package/src/editorInjector/index.js +75 -75
  43. package/src/events.js +634 -622
  44. package/src/helper/clipboard.js +59 -59
  45. package/src/helper/converter.js +586 -564
  46. package/src/helper/dom/domCheck.js +304 -304
  47. package/src/helper/dom/domQuery.js +677 -669
  48. package/src/helper/dom/domUtils.js +618 -557
  49. package/src/helper/dom/index.js +12 -12
  50. package/src/helper/env.js +249 -240
  51. package/src/helper/index.js +25 -25
  52. package/src/helper/keyCodeMap.js +183 -183
  53. package/src/helper/numbers.js +72 -72
  54. package/src/helper/unicode.js +47 -47
  55. package/src/langs/ckb.js +231 -231
  56. package/src/langs/cs.js +231 -231
  57. package/src/langs/da.js +231 -231
  58. package/src/langs/de.js +231 -231
  59. package/src/langs/en.js +230 -230
  60. package/src/langs/es.js +231 -231
  61. package/src/langs/fa.js +231 -231
  62. package/src/langs/fr.js +231 -231
  63. package/src/langs/he.js +231 -231
  64. package/src/langs/hu.js +230 -230
  65. package/src/langs/index.js +28 -28
  66. package/src/langs/it.js +231 -231
  67. package/src/langs/ja.js +230 -230
  68. package/src/langs/km.js +230 -230
  69. package/src/langs/ko.js +230 -230
  70. package/src/langs/lv.js +231 -231
  71. package/src/langs/nl.js +231 -231
  72. package/src/langs/pl.js +231 -231
  73. package/src/langs/pt_br.js +231 -231
  74. package/src/langs/ro.js +231 -231
  75. package/src/langs/ru.js +231 -231
  76. package/src/langs/se.js +231 -231
  77. package/src/langs/tr.js +231 -231
  78. package/src/langs/uk.js +231 -231
  79. package/src/langs/ur.js +231 -231
  80. package/src/langs/zh_cn.js +231 -231
  81. package/src/modules/ApiManager.js +191 -191
  82. package/src/modules/Browser.js +669 -667
  83. package/src/modules/ColorPicker.js +364 -362
  84. package/src/modules/Controller.js +474 -454
  85. package/src/modules/Figure.js +1620 -1617
  86. package/src/modules/FileManager.js +359 -359
  87. package/src/modules/HueSlider.js +577 -565
  88. package/src/modules/Modal.js +346 -346
  89. package/src/modules/ModalAnchorEditor.js +643 -643
  90. package/src/modules/SelectMenu.js +549 -549
  91. package/src/modules/_DragHandle.js +17 -17
  92. package/src/modules/index.js +14 -14
  93. package/src/plugins/browser/audioGallery.js +83 -83
  94. package/src/plugins/browser/fileBrowser.js +103 -103
  95. package/src/plugins/browser/fileGallery.js +83 -83
  96. package/src/plugins/browser/imageGallery.js +81 -81
  97. package/src/plugins/browser/videoGallery.js +103 -103
  98. package/src/plugins/command/blockquote.js +61 -60
  99. package/src/plugins/command/exportPDF.js +134 -134
  100. package/src/plugins/command/fileUpload.js +456 -456
  101. package/src/plugins/command/list_bulleted.js +149 -148
  102. package/src/plugins/command/list_numbered.js +152 -151
  103. package/src/plugins/dropdown/align.js +157 -155
  104. package/src/plugins/dropdown/backgroundColor.js +108 -104
  105. package/src/plugins/dropdown/font.js +141 -137
  106. package/src/plugins/dropdown/fontColor.js +109 -105
  107. package/src/plugins/dropdown/formatBlock.js +170 -178
  108. package/src/plugins/dropdown/hr.js +152 -152
  109. package/src/plugins/dropdown/layout.js +83 -83
  110. package/src/plugins/dropdown/lineHeight.js +131 -130
  111. package/src/plugins/dropdown/list.js +123 -122
  112. package/src/plugins/dropdown/paragraphStyle.js +138 -138
  113. package/src/plugins/dropdown/table.js +4110 -4000
  114. package/src/plugins/dropdown/template.js +83 -83
  115. package/src/plugins/dropdown/textStyle.js +149 -149
  116. package/src/plugins/field/mention.js +242 -242
  117. package/src/plugins/index.js +120 -120
  118. package/src/plugins/input/fontSize.js +414 -410
  119. package/src/plugins/input/pageNavigator.js +71 -70
  120. package/src/plugins/modal/audio.js +677 -677
  121. package/src/plugins/modal/drawing.js +537 -531
  122. package/src/plugins/modal/embed.js +886 -886
  123. package/src/plugins/modal/image.js +1377 -1376
  124. package/src/plugins/modal/link.js +248 -240
  125. package/src/plugins/modal/math.js +563 -563
  126. package/src/plugins/modal/video.js +1226 -1226
  127. package/src/plugins/popup/anchor.js +224 -222
  128. package/src/suneditor.js +114 -107
  129. package/src/themes/dark.css +132 -122
  130. package/src/typedef.js +132 -130
  131. package/types/assets/icons/defaultIcons.d.ts +8 -0
  132. package/types/core/base/eventManager.d.ts +29 -4
  133. package/types/core/class/char.d.ts +2 -1
  134. package/types/core/class/component.d.ts +1 -2
  135. package/types/core/class/format.d.ts +8 -1
  136. package/types/core/class/html.d.ts +8 -0
  137. package/types/core/class/menu.d.ts +8 -0
  138. package/types/core/class/offset.d.ts +24 -26
  139. package/types/core/class/selection.d.ts +2 -0
  140. package/types/core/class/toolbar.d.ts +6 -0
  141. package/types/core/class/ui.d.ts +1 -1
  142. package/types/core/editor.d.ts +34 -12
  143. package/types/core/section/constructor.d.ts +5 -638
  144. package/types/core/section/documentType.d.ts +12 -2
  145. package/types/core/section/options.d.ts +740 -0
  146. package/types/core/util/instanceCheck.d.ts +50 -0
  147. package/types/editorInjector/_core.d.ts +5 -5
  148. package/types/editorInjector/index.d.ts +2 -2
  149. package/types/events.d.ts +2 -0
  150. package/types/helper/converter.d.ts +9 -0
  151. package/types/helper/dom/domQuery.d.ts +5 -5
  152. package/types/helper/dom/domUtils.d.ts +8 -0
  153. package/types/helper/env.d.ts +6 -1
  154. package/types/helper/index.d.ts +4 -1
  155. package/types/index.d.ts +122 -120
  156. package/types/langs/_Lang.d.ts +194 -194
  157. package/types/modules/ColorPicker.d.ts +5 -1
  158. package/types/modules/Controller.d.ts +8 -4
  159. package/types/modules/Figure.d.ts +2 -1
  160. package/types/modules/HueSlider.d.ts +4 -1
  161. package/types/modules/SelectMenu.d.ts +1 -1
  162. package/types/plugins/command/blockquote.d.ts +1 -0
  163. package/types/plugins/command/list_bulleted.d.ts +1 -0
  164. package/types/plugins/command/list_numbered.d.ts +1 -0
  165. package/types/plugins/dropdown/align.d.ts +1 -0
  166. package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
  167. package/types/plugins/dropdown/font.d.ts +1 -0
  168. package/types/plugins/dropdown/fontColor.d.ts +1 -0
  169. package/types/plugins/dropdown/formatBlock.d.ts +3 -2
  170. package/types/plugins/dropdown/lineHeight.d.ts +1 -0
  171. package/types/plugins/dropdown/list.d.ts +1 -0
  172. package/types/plugins/dropdown/table.d.ts +6 -0
  173. package/types/plugins/input/fontSize.d.ts +1 -0
  174. package/types/plugins/modal/drawing.d.ts +4 -0
  175. package/types/plugins/modal/link.d.ts +32 -15
  176. package/types/suneditor.d.ts +13 -9
  177. package/types/typedef.d.ts +8 -0
@@ -1,1617 +1,1620 @@
1
- import EditorInjector from '../editorInjector';
2
- import { Controller, SelectMenu, _DragHandle } from '../modules';
3
- import { dom, numbers, env, converter, keyCodeMap } from '../helper';
4
-
5
- const { ON_OVER_COMPONENT } = env;
6
- const DIRECTION_CURSOR_MAP = { tl: 'nwse-resize', tr: 'nesw-resize', bl: 'nesw-resize', br: 'nwse-resize', lw: 'ew-resize', th: 'ns-resize', rw: 'ew-resize', bh: 'ns-resize' };
7
- const DIR_DIAGONAL = 'tl|bl|tr|br';
8
- const DIR_W = 'lw|rw';
9
- let __resizing_p_wh = false;
10
- let __resizing_p_ow = false;
11
- let __resizing_cw = 0;
12
- let __resizing_sw = 0;
13
-
14
- /**
15
- * @typedef {Object} FigureParams
16
- * @property {string} [sizeUnit="px"] Size unit
17
- * @property {{ current: string, default: string }} [autoRatio=null] Auto ratio { current: '00%', default: '00%' }
18
- */
19
-
20
- /**
21
- * @typedef {Object} FigureInfo
22
- * @property {HTMLElement} target Target element (img, iframe, video, audio, table, etc.)
23
- * @property {HTMLElement} container Container element (div.se-component|span.se-component.se-inline-component)
24
- * @property {?HTMLElement} cover Cover element (FIGURE|null)
25
- * @property {?HTMLElement} inlineCover Inline cover element (span.se-inline-component)
26
- * @property {?HTMLElement} caption Caption element (FIGCAPTION)
27
- */
28
-
29
- /**
30
- * @typedef {Object} FigureTargetInfo
31
- * @property {HTMLElement} container Container element (div.se-component|span.se-component.se-inline-component)
32
- * @property {?HTMLElement=} cover Cover element (FIGURE|null)
33
- * @property {?HTMLElement=} caption Caption element (FIGCAPTION)
34
- * @property {string} [align] - Alignment of the element.
35
- * @property {{w:number, h:number}} [ratio] - The aspect ratio of the element.
36
- * @property {string|number} [w] - Width of the element.
37
- * @property {string|number} [h] - Height of the element.
38
- * @property {number} [t] - Top position.
39
- * @property {number} [l] - Left position.
40
- * @property {string|number} width - Width, can be a number or 'auto'.
41
- * @property {string|number} height - Height, can be a number or 'auto'.
42
- * @property {number} [originWidth] - Original width from `naturalWidth` or `offsetWidth`.
43
- * @property {number} [originHeight] - Original height from `naturalHeight` or `offsetHeight`.
44
- */
45
-
46
- /**
47
- * @typedef {Array<Array<
48
- * string |
49
- * {
50
- * action: (element: Node, value: string, target: Node) => void,
51
- * command: string,
52
- * value: string,
53
- * title: string,
54
- * icon: string
55
- * }
56
- * >>} FigureControls
57
- * "mirror". "rotate", "caption", "revert", "edit", "copy", "remove", "as", "resize_auto,[number]"
58
- */
59
-
60
- /**
61
- * @class
62
- * @description Controller module class
63
- */
64
- class Figure extends EditorInjector {
65
- /**
66
- * @constructor
67
- * @param {*} inst The instance object that called the constructor.
68
- * @param {FigureControls} controls Controller button array
69
- * @param {FigureParams} params Figure options
70
- */
71
- constructor(inst, controls, params) {
72
- super(inst.editor);
73
- this.kind = inst.constructor.key || inst.constructor.name;
74
- this._alignIcons = {
75
- none: this.icons.format_float_none,
76
- left: this.icons.format_float_left,
77
- right: this.icons.format_float_right,
78
- center: this.icons.format_float_center
79
- };
80
-
81
- // modules
82
- /** @type {Object<string, *>} */
83
- this._action = {};
84
- const controllerEl = CreateHTML_controller(this, controls || []);
85
- this.controller = new Controller(this, controllerEl, { position: 'bottom', disabled: true }, this.kind);
86
- // align selectmenu
87
- this.alignButton = controllerEl.querySelector('[data-command="onalign"]');
88
- const alignMenus = CreateAlign(this, this.alignButton);
89
- if (alignMenus) {
90
- this.selectMenu_align = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
91
- this.selectMenu_align.on(this.alignButton, this.#SetMenuAlign.bind(this), { class: 'se-figure-select-list' });
92
- this.selectMenu_align.create(alignMenus.items, alignMenus.html);
93
- }
94
- // as [block, inline] selectmenu
95
- this.asButton = controllerEl.querySelector('[data-command="onas"]');
96
- const asMenus = CreateAs(this, this.asButton);
97
- if (asMenus) {
98
- this.selectMenu_as = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
99
- this.selectMenu_as.on(this.asButton, this.#SetMenuAs.bind(this), { class: 'se-figure-select-list' });
100
- this.selectMenu_as.create(asMenus.items, asMenus.html);
101
- }
102
- // resize selectmenu
103
- this.resizeButton = controllerEl.querySelector('[data-command="onresize"]');
104
- const resizeMenus = CreateResize(this, this.resizeButton);
105
- if (resizeMenus) {
106
- this.selectMenu_resize = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr' });
107
- this.selectMenu_resize.on(this.resizeButton, this.#SetResize.bind(this));
108
- this.selectMenu_resize.create(resizeMenus.items, resizeMenus.html);
109
- }
110
-
111
- // members
112
- this.inst = inst;
113
- this.sizeUnit = params.sizeUnit || 'px';
114
- this.autoRatio = params.autoRatio;
115
- this.isVertical = false;
116
- this.percentageButtons = controllerEl.querySelectorAll('[data-command="resize_percent"]');
117
- this.captionButton = controllerEl.querySelector('[data-command="caption"]');
118
- this.align = 'none';
119
- this.as = 'block';
120
- this._element = null;
121
- this._cover = null;
122
- this._inlineCover = null;
123
- this._container = null;
124
- this._caption = null;
125
- this._width = '';
126
- this._height = '';
127
- this._resize_w = 0;
128
- this._resize_h = 0;
129
- this._element_w = 0;
130
- this._element_h = 0;
131
- this._element_l = 0;
132
- this._element_t = 0;
133
- this._resizeClientX = 0;
134
- this._resizeClientY = 0;
135
- this._resize_direction = '';
136
- this._floatClassStr = '__se__float-none|__se__float-left|__se__float-center|__se__float-right';
137
- this.__preventSizechange = false;
138
- this.__revertSize = { w: '', h: '' };
139
- /** @type {{left?: number, top?: number}} */
140
- this.__offset = {};
141
- this.__offContainer = this.#OffFigureContainer.bind(this);
142
- this.__containerResizing = this.#ContainerResizing.bind(this);
143
- this.__containerResizingOff = this.#ContainerResizingOff.bind(this);
144
- this.__containerResizingESC = this.#ContainerResizingESC.bind(this);
145
- this.__onContainerEvent = null;
146
- this.__offContainerEvent = null;
147
- this.__onResizeESCEvent = null;
148
- this.__fileManagerInfo = false;
149
-
150
- // init
151
- this.eventManager.addEvent(this.alignButton, 'click', this.#OnClick_alignButton.bind(this));
152
- this.eventManager.addEvent(this.asButton, 'click', this.#OnClick_asButton.bind(this));
153
- this.eventManager.addEvent(this.resizeButton, 'click', this.#OnClick_resizeButton.bind(this));
154
- this.editor.applyFrameRoots((e) => {
155
- if (!e.get('wrapper').querySelector('.se-controller.se-resizing-container')) {
156
- // resizing
157
- const main = CreateHTML_resizeDot();
158
- const handles = main.querySelectorAll('.se-resize-dot > span');
159
- e.set('_figure', {
160
- main: main,
161
- border: main.querySelector('.se-resize-dot'),
162
- display: main.querySelector('.se-resize-display'),
163
- handles: handles
164
- });
165
- e.get('wrapper').appendChild(main);
166
- this.eventManager.addEvent(handles, 'mousedown', this.#OnResizeContainer.bind(this));
167
- }
168
- });
169
- }
170
-
171
- /**
172
- * @description Create a container for the resizing component and insert the element.
173
- * @param {Node} element Target element
174
- * @param {string=} className Class name of container (fixed: se-component)
175
- * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
176
- */
177
- static CreateContainer(element, className) {
178
- dom.utils.createElement('DIV', { class: 'se-component' + (className ? ' ' + className : '') }, dom.utils.createElement('FIGURE', null, element));
179
- return Figure.GetContainer(element);
180
- }
181
-
182
- /**
183
- * @description Create a container for the inline resizing component and insert the element.
184
- * @param {Node} element Target element
185
- * @param {string} [className] Class name of container (fixed: se-component se-inline-component)
186
- * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
187
- */
188
- static CreateInlineContainer(element, className) {
189
- dom.utils.createElement('SPAN', { class: 'se-component se-inline-component' + (className ? ' ' + className : '') }, element);
190
- return Figure.GetContainer(element);
191
- }
192
-
193
- /**
194
- * @description Return HTML string of caption(FIGCAPTION) element
195
- * @param {Node} cover Cover element(FIGURE). "CreateContainer().cover"
196
- * @returns {HTMLElement} caption element
197
- */
198
- static CreateCaption(cover, text) {
199
- const caption = dom.utils.createElement('FIGCAPTION', null, '<div>' + text + '</div>');
200
- cover.appendChild(caption);
201
- return caption;
202
- }
203
-
204
- /**
205
- * @description Get the element's container(.se-component) info.
206
- * @param {Node} element Target element
207
- * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
208
- */
209
- static GetContainer(element) {
210
- const cover = dom.query.getParentElement(element, 'FIGURE', 2);
211
- const inlineCover = dom.query.getParentElement(element, 'SPAN', 2);
212
- return {
213
- target: /** @type {HTMLElement} */ (element),
214
- container: /** @type {HTMLElement} */ (dom.query.getParentElement(element, Figure.is, 2) || cover),
215
- cover: /** @type {HTMLElement} */ (cover),
216
- inlineCover: dom.utils.hasClass(inlineCover, 'se-inline-component') ? /** @type {HTMLElement} */ (inlineCover) : null,
217
- caption: /** @type {HTMLElement} */ (dom.query.getEdgeChild(element.parentElement, 'FIGCAPTION', false))
218
- };
219
- }
220
-
221
- /**
222
- * @description Ratio calculation
223
- * @param {string|number} w Width size
224
- * @param {string|number} h Height size
225
- * @param {?string=} [defaultSizeUnit="px"] Default size unit (default: "px")
226
- * @return {{w: number, h: number}}
227
- */
228
- static GetRatio(w, h, defaultSizeUnit) {
229
- let rw = 1,
230
- rh = 1;
231
- if (/\d+/.test(w + '') && /\d+/.test(h + '')) {
232
- const xUnit = (!numbers.is(w) && String(w).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
233
- const yUnit = (!numbers.is(h) && String(h).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
234
- if (xUnit === yUnit) {
235
- const w_number = numbers.get(w, 4);
236
- const h_number = numbers.get(h, 4);
237
- rw = w_number / h_number;
238
- rh = h_number / w_number;
239
- }
240
- }
241
-
242
- return {
243
- w: numbers.get(rw, 4),
244
- h: numbers.get(rh, 4)
245
- };
246
- }
247
-
248
- /**
249
- * @description Ratio calculation
250
- * @param {string|number} w Width size
251
- * @param {string|number} h Height size
252
- * @param {string} defaultSizeUnit Default size unit (default: "px")
253
- * @param {{w: number, h: number}} ratio Ratio size (Figure.GetRatio)
254
- * @return {{w: string|number, h: string|number}}
255
- */
256
- static CalcRatio(w, h, defaultSizeUnit, ratio) {
257
- if (/\d+/.test(w + '') && /\d+/.test(h + '')) {
258
- const xUnit = (!numbers.is(w) && String(w).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
259
- const yUnit = (!numbers.is(h) && String(h).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
260
- if (xUnit === yUnit) {
261
- const dec = xUnit === '%' ? 2 : 0;
262
- const ow = w;
263
- const oh = h;
264
- h = numbers.get(ratio.h * numbers.get(ow, dec), dec) + yUnit;
265
- w = numbers.get(ratio.w * numbers.get(oh, dec), dec) + xUnit;
266
- }
267
- }
268
-
269
- return {
270
- w: w,
271
- h: h
272
- };
273
- }
274
-
275
- /**
276
- * @description It is judged whether it is the component[img, iframe, video, audio, table] cover(class="se-component") and table, hr
277
- * @param {Node} element Target element
278
- * @returns {boolean}
279
- */
280
- static is(element) {
281
- return dom.utils.hasClass(element, 'se-component') || /^(HR)$/.test(element?.nodeName);
282
- }
283
-
284
- /**
285
- * @description Close the figure's controller
286
- */
287
- close() {
288
- this.editor._preventBlur = false;
289
- dom.utils.removeClass(this._cover, 'se-figure-selected');
290
- this.controller.close();
291
- this.component._removeDragEvent();
292
- }
293
-
294
- /**
295
- * @description Open the figure's controller
296
- * @param {Node} targetNode Target element
297
- * @param {Object} params params
298
- * @param {boolean} [params.nonResizing=false] Do not display the resizing button
299
- * @param {boolean} [params.nonSizeInfo=false] Do not display the size information
300
- * @param {boolean} [params.nonBorder=false] Do not display the selected style line
301
- * @param {boolean} [params.figureTarget=false] If true, the target is a figure element
302
- * @param {boolean} [params.__fileManagerInfo=false] If true, the file manager is called
303
- * @returns {FigureTargetInfo|undefined} figure target info
304
- */
305
- open(targetNode, { nonResizing, nonSizeInfo, nonBorder, figureTarget, __fileManagerInfo }) {
306
- if (!targetNode) {
307
- console.warn('[SUNEDITOR.modules.Figure.open] The "targetNode" is null.');
308
- return;
309
- }
310
-
311
- if (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
312
- this.ui._offCurrentController();
313
- } else {
314
- nonBorder = true;
315
- }
316
-
317
- const figureInfo = Figure.GetContainer(targetNode);
318
- const target = figureInfo.target;
319
- let exceptionFormat = false;
320
- if (!figureInfo.container) {
321
- if (!this.options.get('strictMode').formatFilter) {
322
- figureInfo.container = target;
323
- figureInfo.cover = target;
324
- exceptionFormat = true;
325
- } else {
326
- return {
327
- container: null,
328
- cover: null,
329
- width: target.style.width || (!numbers.is(/** @type {HTMLImageElement} */ (target).width) ? /** @type {HTMLImageElement} */ (target).width : '') || '',
330
- height: target.style.height || (!numbers.is(/** @type {HTMLImageElement} */ (target).height) ? /** @type {HTMLImageElement} */ (target).height : '') || ''
331
- };
332
- }
333
- }
334
-
335
- _DragHandle.set('__figureInst', this);
336
-
337
- this._setFigureInfo(figureInfo);
338
-
339
- const sizeTarget = /** @type {HTMLElement} */ (figureTarget ? this._cover || this._container || target : target);
340
- const w = sizeTarget.offsetWidth || null;
341
- const h = sizeTarget.offsetHeight || null;
342
- const { top, left, scrollX, scrollY } = this.offset.getLocal(sizeTarget);
343
-
344
- const dataSize = (target.getAttribute('data-se-size') || '').split(',');
345
- const ratio = Figure.GetRatio(dataSize[0] || numbers.get(target.style.width, 2) || w, dataSize[1] || numbers.get(target.style.height, 2) || h, this.sizeUnit);
346
- const targetInfo = {
347
- container: figureInfo.container,
348
- cover: figureInfo.cover,
349
- caption: figureInfo.caption,
350
- align: this.align,
351
- ratio: ratio,
352
- w: w || '',
353
- h: h || '',
354
- t: top,
355
- l: left,
356
- width: dataSize[0] || 'auto',
357
- height: dataSize[1] || 'auto',
358
- originWidth: /** @type {HTMLImageElement} */ (target).naturalWidth || target.offsetWidth,
359
- originHeight: /** @type {HTMLImageElement} */ (target).naturalHeight || target.offsetHeight
360
- };
361
-
362
- this._width = targetInfo.width;
363
- this._height = targetInfo.height;
364
- if (__fileManagerInfo || this.__fileManagerInfo) return targetInfo;
365
-
366
- const _figure = this.editor.frameContext.get('_figure');
367
- this.editor._figureContainer = _figure.main;
368
- _figure.main.style.top = top + 'px';
369
- _figure.main.style.left = left + 'px';
370
- _figure.main.style.width = (this.isVertical ? h : w) + 'px';
371
- _figure.main.style.height = (this.isVertical ? w : h) + 'px';
372
- _figure.border.style.top = '0px';
373
- _figure.border.style.left = '0px';
374
- _figure.border.style.width = (this.isVertical ? h : w) + 'px';
375
- _figure.border.style.height = (this.isVertical ? w : h) + 'px';
376
-
377
- this.__offset = { left: left + scrollX, top: top + scrollY };
378
- this.editor.opendControllers.push({
379
- position: 'none',
380
- form: _figure.main,
381
- target: sizeTarget,
382
- inst: this,
383
- notInCarrier: true
384
- });
385
-
386
- // percentage active
387
- const value = /%$/.test(target.style.width) && /%$/.test(figureInfo.container.style.width) ? numbers.get(figureInfo.container.style.width, 0) / 100 + '' : '';
388
- for (let i = 0, len = this.percentageButtons.length; i < len; i++) {
389
- if (this.percentageButtons[i].getAttribute('data-value') === value) {
390
- dom.utils.addClass(this.percentageButtons[i], 'active');
391
- } else {
392
- dom.utils.removeClass(this.percentageButtons[i], 'active');
393
- }
394
- }
395
-
396
- // caption active
397
- if (this.captionButton) {
398
- if (figureInfo.caption) {
399
- dom.utils.addClass(this.captionButton, 'active');
400
- } else {
401
- dom.utils.removeClass(this.captionButton, 'active');
402
- }
403
- }
404
-
405
- _figure.display.style.display = nonSizeInfo || this._inlineCover ? 'none' : '';
406
- _figure.border.style.display = nonBorder ? 'none' : '';
407
- _figure.main.style.display = 'block';
408
-
409
- if (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
410
- // align button
411
- this._setAlignIcon();
412
- // as button
413
- this._setAsIcon();
414
- this.ui._visibleControllers(true, true);
415
- // size
416
- const size = this.getSize(target);
417
- dom.utils.changeTxt(_figure.display, this.lang[this.align === 'none' ? 'basic' : this.align] + ' (' + size.w + ', ' + size.h + ')');
418
- this._displayResizeHandles(!nonResizing);
419
- // rotate, aption, align, onresize - display;
420
- const transformButtons = this.controller.form.querySelectorAll(
421
- '[data-command="rotate"][data-value="90"], [data-command="rotate"][data-value="-90"], [data-command="caption"], [data-command="onalign"], [data-command="onresize"]'
422
- );
423
- const display = this._inlineCover || exceptionFormat ? 'none' : '';
424
- transformButtons.forEach((button) => {
425
- /** @type {HTMLButtonElement} */ (button).style.display = display;
426
- });
427
- // onas
428
- const onas = this.controller.form.querySelector('[data-command="onas"]');
429
- if (onas) {
430
- /** @type {HTMLButtonElement} */ (onas).style.display = exceptionFormat ? 'none' : '';
431
- }
432
- // selecte
433
- dom.utils.removeClass(this._cover, 'se-figure-over-selected');
434
- this.controller.open(_figure.main, null, { initMethod: this.__offContainer, isWWTarget: false, addOffset: null });
435
- this._w.setTimeout(() => _DragHandle.set('__overInfo', false), 0);
436
- } else {
437
- dom.utils.addClass(this._cover, 'se-figure-over-selected');
438
- }
439
-
440
- // set members
441
- dom.utils.addClass(this._cover, 'se-figure-selected');
442
- this._element_w = this._resize_w = w;
443
- this._element_h = this._resize_h = h;
444
- this._element_l = left;
445
- this._element_t = top;
446
-
447
- // drag
448
- if (!this._inlineCover && (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT || dom.utils.hasClass(figureInfo.container, 'se-input-component'))) {
449
- this._setDragEvent(_figure.main);
450
- }
451
-
452
- return targetInfo;
453
- }
454
-
455
- /**
456
- * @description Hide the controller
457
- */
458
- controllerHide() {
459
- this.controller.hide();
460
- }
461
-
462
- /**
463
- * @description Hide the controller
464
- */
465
- controllerShow() {
466
- this.controller.show();
467
- }
468
-
469
- /**
470
- * @description Open the figure's controller
471
- * @param {Node} target Target element
472
- * @param {Object} [params={}] params
473
- * @param {boolean=} params.isWWTarget If the controller is in the WYSIWYG area, set it to true.
474
- * @param {() => void=} params.initMethod Method to be called when the controller is closed.
475
- * @param {boolean=} params.disabled If true, the controller is disabled.
476
- * @param {{left: number, top: number}=} params.addOffset Additional offset values
477
- */
478
- controllerOpen(target, params) {
479
- this._element = /** @type {HTMLElement} */ (target);
480
- this.controller.open(target, null, params);
481
- }
482
-
483
- /**
484
- * @description Set the element's container size
485
- * @param {string|number} w Width size
486
- * @param {string|number} h Height size
487
- */
488
- setSize(w, h) {
489
- if (/%$/.test(w + '')) {
490
- this._setPercentSize(w, h);
491
- } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
492
- if (this.autoRatio) this._setPercentSize(100, this.autoRatio.default || this.autoRatio.current);
493
- else this._setAutoSize();
494
- } else {
495
- this._applySize(w, h, '');
496
- }
497
- }
498
-
499
- /**
500
- * @description Gets the Figure size
501
- * @param {?Node=} targetNode Target element, default is the current element
502
- * @returns {{w: string, h: string}}
503
- */
504
- getSize(targetNode) {
505
- if (!targetNode) targetNode = this._element;
506
- if (!targetNode) return { w: '', h: '' };
507
-
508
- const figure = Figure.GetContainer(targetNode);
509
- const target = figure.target;
510
- if (!figure.container) {
511
- // exceptionFormat
512
- if (!this.options.get('strictMode').formatFilter) {
513
- return {
514
- w: target.style.width || 'auto',
515
- h: target.style.height || 'auto'
516
- };
517
- }
518
- return {
519
- w: '',
520
- h: target.style.height
521
- };
522
- }
523
-
524
- const w = !/%$/.test(target.style.width) ? target.style.width : ((figure.container && numbers.get(figure.container.style.width, 2)) || 100) + '%';
525
- const h = figure.inlineCover
526
- ? figure.inlineCover.style.height
527
- : numbers.get(figure.cover.style.paddingBottom, 0) > 0 && !this.isVertical
528
- ? figure.cover.style.height
529
- : !/%$/.test(target.style.height) || !/%$/.test(target.style.width)
530
- ? target.style.height
531
- : ((figure.container && numbers.get(figure.container.style.height, 2)) || 100) + '%';
532
- return {
533
- w: w || 'auto',
534
- h: h || 'auto'
535
- };
536
- }
537
-
538
- /**
539
- * @description Align the container.
540
- * @param {?Node} targetNode Target element
541
- * @param {string} align "none"|"left"|"center"|"right"
542
- */
543
- setAlign(targetNode, align) {
544
- if (!targetNode) targetNode = this._element;
545
- this.align = align = align || 'none';
546
-
547
- const figure = Figure.GetContainer(targetNode);
548
- if (!figure.cover) return;
549
-
550
- const target = figure.target;
551
- const container = figure.container;
552
- const cover = figure.cover;
553
- if (/%$/.test(target.style.width) && align === 'center' && !this.component.isInline(container)) {
554
- container.style.minWidth = '100%';
555
- cover.style.width = container.style.width;
556
- } else {
557
- container.style.minWidth = '';
558
- cover.style.width = this.isVertical ? target.style.height || target.offsetHeight + 'px' : !target.style.width || target.style.width === 'auto' ? '' : target.style.width || '100%';
559
- }
560
-
561
- if (!dom.utils.hasClass(container, '__se__float-' + align)) {
562
- dom.utils.removeClass(container, this._floatClassStr);
563
- dom.utils.addClass(container, '__se__float-' + align);
564
- }
565
-
566
- if (this.autoRatio) {
567
- const { w, h } = this.getSize(this._element);
568
- this.__setCoverPaddingBottom(w, h);
569
- }
570
-
571
- this._setAlignIcon();
572
- }
573
-
574
- /**
575
- * @description As style[block, inline] the component
576
- * @param {?Node} targetNode Target element
577
- * @param {"block"|"inline"} formatStyle Format style
578
- */
579
- convertAsFormat(targetNode, formatStyle) {
580
- if (!targetNode) targetNode = this._element;
581
- this.as = formatStyle || 'block';
582
- const { container, inlineCover, target } = Figure.GetContainer(targetNode);
583
- const { w, h } = this.getSize(target);
584
-
585
- const newTarget = /** @type {HTMLElement} */ (target.cloneNode(false));
586
-
587
- switch (formatStyle) {
588
- case 'inline': {
589
- if (inlineCover) break;
590
- this.component.deselect();
591
-
592
- const next = container.nextElementSibling;
593
- const parent = container.parentElement;
594
-
595
- newTarget.style.width = '';
596
- newTarget.style.height = '';
597
- const figure = Figure.CreateInlineContainer(newTarget);
598
- dom.utils.addClass(
599
- figure.container,
600
- container.className
601
- .split(' ')
602
- .filter((v) => v !== 'se-figure-selected' && v !== 'se-component-selected')
603
- .join('|')
604
- );
605
-
606
- this._asFormatChange(figure, w, h);
607
-
608
- const line = dom.utils.createElement(this.options.get('defaultLine'), null, figure.container);
609
- parent.insertBefore(line, next);
610
- dom.utils.removeItem(container);
611
-
612
- break;
613
- }
614
- case 'block': {
615
- if (!inlineCover) break;
616
- this.component.deselect();
617
-
618
- this.selection.setRange(container, 0, container, 1);
619
- const r = this.html.remove();
620
- const s = this.nodeTransform.split(r.container, r.offset, 0);
621
-
622
- if (s?.previousElementSibling && dom.check.isZeroWidth(s.previousElementSibling)) {
623
- dom.utils.removeItem(s.previousElementSibling);
624
- }
625
-
626
- newTarget.style.width = '';
627
- newTarget.style.height = '';
628
- const figure = Figure.CreateContainer(newTarget);
629
- dom.utils.addClass(
630
- figure.container,
631
- container.className
632
- .split(' ')
633
- .filter((v) => v !== 'se-inline-component' && v !== 'se-figure-selected' && v !== 'se-component-selected')
634
- .join('|')
635
- );
636
-
637
- this._asFormatChange(figure, w, h);
638
-
639
- (s || r.container).parentElement.insertBefore(figure.container, s);
640
-
641
- break;
642
- }
643
- }
644
- }
645
-
646
- /**
647
- * @private
648
- * @description Handles format conversion (block/inline) for the figure component and applies size changes.
649
- * @param {FigureInfo} figureinfo {target, container, cover, inlineCover, caption}
650
- * @param {string|number} w Width value.
651
- * @param {string|number} h Height value.
652
- */
653
- _asFormatChange(figureinfo, w, h) {
654
- const kind = this.kind;
655
- figureinfo.target.onload = () => this.component.select(figureinfo.target, kind);
656
-
657
- this._setFigureInfo(figureinfo);
658
-
659
- if (figureinfo.inlineCover) {
660
- this.setAlign(figureinfo.target, 'none');
661
- this.deleteTransform();
662
- }
663
-
664
- this.setSize(w, h);
665
- }
666
-
667
- /**
668
- * @description Controller button action
669
- * @param {HTMLButtonElement} target Target button element
670
- * @returns
671
- */
672
- controllerAction(target) {
673
- const command = target.getAttribute('data-command');
674
- const value = target.getAttribute('data-value');
675
- const type = target.getAttribute('data-type');
676
- const element = this._element;
677
- if (/^on.+/.test(command) || type === 'selectMenu') return;
678
-
679
- switch (command) {
680
- case 'mirror': {
681
- const info = this.#GetRotateValue(element);
682
- let x = info.x;
683
- let y = info.y;
684
-
685
- if ((value === 'h' && !this.isVertical) || (value === 'v' && this.isVertical)) {
686
- y = y ? '' : '180';
687
- } else {
688
- x = x ? '' : '180';
689
- }
690
-
691
- this._setRotate(element, info.r, x, y);
692
- break;
693
- }
694
- case 'rotate':
695
- this.setTransform(element, null, null, value);
696
- break;
697
- case 'caption':
698
- if (!this._caption) {
699
- const caption = Figure.CreateCaption(this._cover, this.lang.caption);
700
- const captionText = dom.query.getEdgeChild(caption, (current) => current.nodeType === 3, false);
701
-
702
- if (!captionText) {
703
- caption.focus();
704
- } else {
705
- this.selection.setRange(captionText, 0, captionText, captionText.textContent.length);
706
- }
707
-
708
- this.controller.close();
709
- } else {
710
- dom.utils.removeItem(this._caption);
711
- this._w.setTimeout(this.component.select.bind(this.component, element, this.kind), 0);
712
- }
713
-
714
- this._caption = !this._caption;
715
- if (/\d+/.test(element.style.height) || (this.isVertical && this._caption)) {
716
- if (/%$/.test(element.style.width) || /auto/.test(element.style.height)) {
717
- this.deleteTransform();
718
- } else {
719
- this.setTransform(element, element.style.width, element.style.height, 0);
720
- }
721
- }
722
- break;
723
- case 'revert':
724
- this._setRevert();
725
- break;
726
- case 'edit':
727
- this.inst.edit(element);
728
- break;
729
- case 'copy': {
730
- this.component.copy(this._container);
731
- break;
732
- }
733
- case 'remove':
734
- this.inst.destroy(element);
735
- this.controller.close();
736
- break;
737
- }
738
-
739
- if (/^__c__/.test(command)) {
740
- this._action[command](element, value, target);
741
- return;
742
- }
743
-
744
- if (/^edit$/.test(command)) return;
745
-
746
- this.history.push(false);
747
- if (!/^remove|caption$/.test(command)) {
748
- this.component.select(element, this.kind);
749
- }
750
- }
751
-
752
- /**
753
- * @description Inspect the figure component format and change it to the correct format.
754
- * @param {Node} container - The container element of the figure component.
755
- * @param {Node} originEl - The original element of the figure component.
756
- * @param {Node} anchorCover - The anchor cover element of the figure component.
757
- * @param {import('./FileManager').default} [fileManagerInst=null] - FileManager module instance, if used.
758
- */
759
- retainFigureFormat(container, originEl, anchorCover, fileManagerInst) {
760
- const isInline = this.component.isInline(container);
761
- let existElement = this.format.isBlock(originEl.parentNode) || dom.check.isWysiwygFrame(originEl.parentNode) ? originEl : dom.check.isAnchor(originEl.parentNode) ? originEl.parentNode : this.format.getLine(originEl) || originEl;
762
-
763
- if (dom.query.getParentElement(originEl, dom.check.isExcludeFormat)) {
764
- existElement = anchorCover && anchorCover !== originEl ? anchorCover : originEl;
765
- existElement.parentNode.replaceChild(container, existElement);
766
- } else if (isInline && this.format.isLine(existElement)) {
767
- const refer = isInline && /^SPAN$/i.test(originEl.parentElement.nodeName) ? originEl.parentElement : originEl;
768
- refer.parentElement.replaceChild(container, refer);
769
- } else if (dom.check.isListCell(existElement)) {
770
- const refer = dom.query.getParentElement(originEl, (current) => current.parentNode === existElement);
771
- existElement.insertBefore(container, refer);
772
- dom.utils.removeItem(originEl);
773
- this.nodeTransform.removeEmptyNode(refer, null, true);
774
- } else if (this.format.isLine(existElement)) {
775
- const refer = dom.query.getParentElement(originEl, (current) => current.parentNode === existElement);
776
- existElement = this.nodeTransform.split(existElement, refer);
777
- existElement.parentNode.insertBefore(container, existElement);
778
- dom.utils.removeItem(originEl);
779
- this.nodeTransform.removeEmptyNode(existElement, null, true);
780
- } else {
781
- if (this.format.isLine(existElement.parentNode)) {
782
- const formats = existElement.parentNode;
783
- formats.parentNode.insertBefore(container, existElement.previousSibling ? formats.nextElementSibling : formats);
784
- if (fileManagerInst?.__updateTags.map((current) => existElement.contains(current)).length === 0) dom.utils.removeItem(existElement);
785
- else if (dom.check.isZeroWidth(existElement)) dom.utils.removeItem(existElement);
786
- } else {
787
- existElement = dom.check.isFigure(existElement.parentNode) ? dom.query.getParentElement(existElement.parentNode, Figure.is) : existElement;
788
- existElement.parentNode.replaceChild(container, existElement);
789
- }
790
- }
791
- }
792
-
793
- /**
794
- * @description Initialize the transform style (rotation) of the element.
795
- * @param {?Node=} node Target element, default is the current element
796
- */
797
- deleteTransform(node) {
798
- if (!node) node = this._element;
799
-
800
- const element = /** @type {HTMLElement} */ (node);
801
- const size = (element.getAttribute('data-se-size') || '').split(',');
802
- this.isVertical = false;
803
-
804
- element.style.maxWidth = '';
805
- element.style.transform = '';
806
- element.style.transformOrigin = '';
807
-
808
- this._deleteCaptionPosition(element);
809
- this._applySize(size[0] || 'auto', size[1] || '', '');
810
- }
811
-
812
- /**
813
- * @description Set the transform style (rotation) of the element.
814
- * @param {Node} node Target element
815
- * @param {?string|number} width Element's width size
816
- * @param {?string|number} height Element's height size
817
- */
818
- setTransform(node, width, height, deg) {
819
- try {
820
- this.__preventSizechange = true;
821
- const info = this.#GetRotateValue(node);
822
- const slope = info.r + (deg || 0) * 1;
823
- deg = Math.abs(slope) >= 360 ? 0 : slope;
824
- const isVertical = (this.isVertical = /^(90|270)$/.test(Math.abs(deg).toString()));
825
-
826
- width = numbers.get(width, 0);
827
- height = numbers.get(height, 0);
828
-
829
- const element = /** @type {HTMLElement} */ (node);
830
- const dataSize = (element.getAttribute('data-se-size') || 'auto,auto').split(',');
831
- let transOrigin = '';
832
- if (/auto|%$/.test(dataSize[0]) && !isVertical) {
833
- if (dataSize[0] === 'auto' && dataSize[1] === 'auto') {
834
- this._setAutoSize();
835
- } else {
836
- this._setPercentSize(dataSize[0], dataSize[1]);
837
- }
838
- } else {
839
- const figureInfo = Figure.GetContainer(element);
840
- const cover = figureInfo.cover || figureInfo.inlineCover;
841
- const offsetW = width || element.offsetWidth;
842
- const offsetH = height || element.offsetHeight;
843
- const w = (isVertical ? offsetH : offsetW) + 'px';
844
- const h = (isVertical ? offsetW : offsetH) + 'px';
845
-
846
- this._deletePercentSize();
847
- this._applySize(offsetW + 'px', offsetH + 'px', '');
848
-
849
- cover.style.width = w;
850
- cover.style.height = figureInfo.caption || figureInfo.inlineCover ? '' : h;
851
-
852
- if (isVertical) {
853
- const transW = offsetW / 2 + 'px ' + offsetW / 2 + 'px 0';
854
- const transH = offsetH / 2 + 'px ' + offsetH / 2 + 'px 0';
855
- transOrigin = deg === 90 || deg === -270 ? transH : transW;
856
- }
857
- }
858
-
859
- element.style.transformOrigin = transOrigin;
860
- this._setRotate(element, deg, info.x, info.y);
861
-
862
- if (isVertical) element.style.maxWidth = 'none';
863
- else element.style.maxWidth = '';
864
-
865
- this._setCaptionPosition(element);
866
- } finally {
867
- this.__preventSizechange = false;
868
- }
869
- }
870
-
871
- /**
872
- * @private
873
- * @description Sets figure component properties such as cover, container, caption, and alignment.
874
- * @param {FigureInfo} figureInfo - {target, container, cover, inlineCover, caption}
875
- */
876
- _setFigureInfo(figureInfo) {
877
- this._inlineCover = figureInfo.inlineCover;
878
- this._cover = figureInfo.cover || this._inlineCover;
879
- this._container = figureInfo.container;
880
- this._caption = figureInfo.caption;
881
- this._element = figureInfo.target;
882
- this.align = (this._container.className.match(/(?:^|\s)__se__float-(none|left|center|right)(?:$|\s)/) || [])[1] || figureInfo.target.style.float || 'none';
883
- this.as = this._inlineCover ? 'inline' : 'block';
884
- this.isVertical = /^(90|270)$/.test(Math.abs(this.#GetRotateValue(figureInfo.target).r).toString());
885
- }
886
-
887
- /**
888
- * @private
889
- * @description Applies rotation transformation to the target element.
890
- * @param {HTMLElement} element Target element.
891
- * @param {number} r Rotation degree.
892
- * @param {number} x X-axis rotation value.
893
- * @param {number} y Y-axis rotation value.
894
- */
895
- _setRotate(element, r, x, y) {
896
- let width = (element.offsetWidth - element.offsetHeight) * (/^-/.test(r + '') ? 1 : -1);
897
- let translate = '';
898
-
899
- if (/[1-9]/.test(r + '') && (x || y)) {
900
- translate = x ? 'Y' : 'X';
901
-
902
- switch (r + '') {
903
- case '90':
904
- translate = x && y ? 'X' : y ? translate : '';
905
- break;
906
- case '270':
907
- width *= -1;
908
- translate = x && y ? 'Y' : x ? translate : '';
909
- break;
910
- case '-90':
911
- translate = x && y ? 'Y' : x ? translate : '';
912
- break;
913
- case '-270':
914
- width *= -1;
915
- translate = x && y ? 'X' : y ? translate : '';
916
- break;
917
- default:
918
- translate = '';
919
- }
920
- }
921
-
922
- if (r % 180 === 0) {
923
- element.style.maxWidth = '';
924
- }
925
-
926
- element.style.transform = 'rotate(' + r + 'deg)' + (x ? ' rotateX(' + x + 'deg)' : '') + (y ? ' rotateY(' + y + 'deg)' : '') + (translate ? ' translate' + translate + '(' + width + 'px)' : '');
927
- }
928
-
929
- /**
930
- * @private
931
- * @description Applies size adjustments to the figure element.
932
- * @param {string|number} w Width value.
933
- * @param {string|number} h Height value.
934
- * @param {string} direction Resize direction.
935
- */
936
- _applySize(w, h, direction) {
937
- const onlyW = /^(rw|lw)$/.test(direction) && /\d+/.test(this._element.style.height);
938
- const onlyH = /^(th|bh)$/.test(direction) && /\d+/.test(this._element.style.width);
939
- h = /** @type {string} */ (h || (this.autoRatio ? this.autoRatio.current || this.autoRatio.default : h));
940
- w = /** @type {string} */ (numbers.is(w) ? w + this.sizeUnit : w);
941
-
942
- if (!/%$/.test(w) && !/%$/.test(h) && !onlyW && !onlyH) this._deletePercentSize();
943
-
944
- const sizeTarget = this._cover || this._element;
945
-
946
- if (this.autoRatio) this._cover.style.width = w;
947
- if (!onlyH) {
948
- sizeTarget.style.width = this._element.style.width = w;
949
- }
950
- if (!onlyW) {
951
- h = numbers.is(h) ? h + this.sizeUnit : h;
952
- sizeTarget.style.height = this._element.style.height = this.autoRatio && !this.isVertical ? '100%' : h;
953
- if (this.autoRatio) {
954
- this._cover.style.height = h;
955
- this.__setCoverPaddingBottom(w, h);
956
- }
957
- }
958
-
959
- if (this.align === 'center') this.setAlign(this._element, this.align);
960
-
961
- // save current size
962
- this._saveCurrentSize();
963
- }
964
-
965
- /**
966
- * @private
967
- * @description Sets padding-bottom for cover elements based on width and height.
968
- * @param {string} w Width value.
969
- * @param {string} h Height value.
970
- */
971
- __setCoverPaddingBottom(w, h) {
972
- if (this._inlineCover === this._cover) return;
973
-
974
- this._cover.style.height = h;
975
- if (/%$/.test(w) && this.align === 'center') {
976
- this._cover.style.paddingBottom = !/%$/.test(h) ? h : numbers.get((numbers.get(h, 2) / 100) * numbers.get(w, 2), 2) + '%';
977
- } else {
978
- this._cover.style.paddingBottom = h;
979
- }
980
- }
981
-
982
- /**
983
- * @private
984
- * @description Sets the figure element to its auto size.
985
- */
986
- _setAutoSize() {
987
- if (this._caption) this._caption.style.marginTop = '';
988
- this.deleteTransform();
989
- this._deletePercentSize();
990
-
991
- if (this.autoRatio) {
992
- this._setPercentSize('100%', this.autoRatio.current || this.autoRatio.default);
993
- } else {
994
- this._element.style.maxWidth = '';
995
- this._element.style.width = '';
996
- this._element.style.height = '';
997
- this._cover.style.width = '';
998
- this._cover.style.height = '';
999
- }
1000
-
1001
- this.setAlign(this._element, this.align);
1002
-
1003
- // save current size
1004
- this._saveCurrentSize();
1005
- }
1006
-
1007
- /**
1008
- * @private
1009
- * @description Sets the figure element's size in percentage.
1010
- * @param {string|number} w Width percentage.
1011
- * @param {string|number} h Height percentage.
1012
- */
1013
- _setPercentSize(w, h) {
1014
- if (!h) h = this.autoRatio ? (/%$/.test(this.autoRatio.current) ? this.autoRatio.current : this.autoRatio.default) : h;
1015
- h = h && !/%$/.test(h + '') && !numbers.get(h, 0) ? (numbers.is(h) ? h + '%' : h) : numbers.is(h) ? h + this.sizeUnit : h || (this.autoRatio ? this.autoRatio.default : '');
1016
-
1017
- const heightPercentage = /%$/.test(h + '');
1018
- this._container.style.width = String(numbers.is(w) ? w + '%' : w);
1019
- this._container.style.height = '';
1020
-
1021
- // exceptionFormat
1022
- if (this._element === this._cover && !this.options.get('strictMode').formatFilter) {
1023
- this._saveCurrentSize();
1024
- return;
1025
- }
1026
-
1027
- if (this._inlineCover !== this._cover) {
1028
- this._cover.style.width = '100%';
1029
- this._cover.style.height = String(h);
1030
- }
1031
- this._element.style.width = '100%';
1032
- this._element.style.maxWidth = '';
1033
- this._element.style.height = String(this.autoRatio ? '100%' : heightPercentage ? '' : h);
1034
-
1035
- if (this.align === 'center') this.setAlign(this._element, this.align);
1036
- if (this.autoRatio) {
1037
- this.__setCoverPaddingBottom(String(w), String(h));
1038
- }
1039
-
1040
- this._setCaptionPosition(this._element);
1041
-
1042
- // save current size
1043
- this._saveCurrentSize();
1044
- }
1045
-
1046
- /**
1047
- * @private
1048
- * @description Deletes percentage-based sizing from the figure element.
1049
- */
1050
- _deletePercentSize() {
1051
- this._cover.style.width = '';
1052
- this._cover.style.height = '';
1053
- this._container.style.width = '';
1054
- this._container.style.height = '';
1055
-
1056
- dom.utils.removeClass(this._container, this._floatClassStr);
1057
- dom.utils.addClass(this._container, '__se__float-' + this.align);
1058
-
1059
- if (this.align === 'center') this.setAlign(this._element, this.align);
1060
- }
1061
-
1062
- /**
1063
- * @private
1064
- * @description Reverts the figure element to its previously saved size.
1065
- */
1066
- _setRevert() {
1067
- this.setSize(this.__revertSize.w, this.__revertSize.h);
1068
- }
1069
-
1070
- /**
1071
- * @private
1072
- * @description Updates the figure's alignment icon.
1073
- */
1074
- _setAlignIcon() {
1075
- if (!this.alignButton) return;
1076
- dom.utils.changeElement(this.alignButton.firstElementChild, this._alignIcons[this.align]);
1077
- }
1078
-
1079
- /**
1080
- * @private
1081
- * @description Updates the figure's block/inline format icon.
1082
- */
1083
- _setAsIcon() {
1084
- if (!this.asButton) return;
1085
- dom.utils.changeElement(this.asButton.firstElementChild, this.icons[`as_${this.as}`]);
1086
- }
1087
-
1088
- /**
1089
- * @private
1090
- * @description Saves the current size of the figure component.
1091
- */
1092
- _saveCurrentSize() {
1093
- if (this.__preventSizechange) return;
1094
-
1095
- const dataSize = (this._element.getAttribute('data-se-size') || ',').split(',');
1096
- this.__revertSize.w = dataSize[0];
1097
- this.__revertSize.h = dataSize[1];
1098
-
1099
- const size = this.getSize(this._element);
1100
- // add too width, height attribute
1101
- this._element.setAttribute('width', size.w.replace('px', ''));
1102
- this._element.setAttribute('height', size.h.replace('px', ''));
1103
- this._element.setAttribute('data-se-size', size.w + ',' + size.h);
1104
- if (this.autoRatio) {
1105
- this.autoRatio.current = /%$/.test(size.h) ? size.h : '';
1106
- }
1107
- }
1108
-
1109
- /**
1110
- * @private
1111
- * @description Adjusts the position of the caption within the figure.
1112
- * @param {HTMLElement} element Target element.
1113
- */
1114
- _setCaptionPosition(element) {
1115
- const figcaption = /** @type {HTMLElement} */ (dom.query.getEdgeChild(dom.query.getParentElement(element, 'FIGURE'), 'FIGCAPTION', false));
1116
- if (figcaption) {
1117
- figcaption.style.marginTop = (this.isVertical ? element.offsetWidth - element.offsetHeight : 0) + 'px';
1118
- }
1119
- }
1120
-
1121
- /**
1122
- * @private
1123
- * @description Removes the margin top property from the figure caption.
1124
- * @param {HTMLElement} element Target element.
1125
- */
1126
- _deleteCaptionPosition(element) {
1127
- const figcaption = /** @type {HTMLElement} */ (dom.query.getEdgeChild(dom.query.getParentElement(element, 'FIGURE'), 'FIGCAPTION', false));
1128
- if (figcaption) {
1129
- figcaption.style.marginTop = '';
1130
- }
1131
- }
1132
-
1133
- /**
1134
- * @private
1135
- * @description Displays or hides the resize handles of the figure component.
1136
- * @param {boolean} display Whether to display resize handles.
1137
- */
1138
- _displayResizeHandles(display) {
1139
- const type = !display ? 'none' : 'flex';
1140
- this.controller.form.style.display = type;
1141
-
1142
- const _figure = this.editor.frameContext.get('_figure');
1143
- const resizeHandles = _figure.handles;
1144
- for (let i = 0, len = resizeHandles.length; i < len; i++) {
1145
- resizeHandles[i].style.display = type;
1146
- }
1147
-
1148
- if (type === 'none') {
1149
- dom.utils.addClass(_figure.main, 'se-resize-ing');
1150
- this.__onResizeESCEvent = this.eventManager.addGlobalEvent('keydown', this.__containerResizingESC);
1151
- } else {
1152
- dom.utils.removeClass(_figure.main, 'se-resize-ing');
1153
- }
1154
- }
1155
-
1156
- /**
1157
- * @private
1158
- * @description Removes the resize event listeners.
1159
- */
1160
- _offResizeEvent() {
1161
- this.component._removeDragEvent();
1162
- this.eventManager.removeGlobalEvent(this.__onContainerEvent);
1163
- this.eventManager.removeGlobalEvent(this.__offContainerEvent);
1164
- this.eventManager.removeGlobalEvent(this.__onResizeESCEvent);
1165
-
1166
- this._displayResizeHandles(true);
1167
- this.ui._offCurrentController();
1168
- this.ui.disableBackWrapper();
1169
- }
1170
-
1171
- /**
1172
- * @private
1173
- * @description Sets up drag event handling for the figure component.
1174
- * @param {Node} figureMain The main figure container element.
1175
- */
1176
- _setDragEvent(figureMain) {
1177
- const dragHandle = this.editor.frameContext.get('wrapper').querySelector('.se-drag-handle');
1178
- dom.utils.removeClass(dragHandle, 'se-drag-handle-full');
1179
-
1180
- dragHandle.style.opacity = '';
1181
- dragHandle.style.width = '';
1182
- dragHandle.style.height = '';
1183
-
1184
- _DragHandle.set('__dragHandler', dragHandle);
1185
- _DragHandle.set('__dragContainer', this._container);
1186
- _DragHandle.set('__dragCover', this._cover);
1187
- _DragHandle.set('__dragMove', this.#OnScrollDragHandler.bind(this, dragHandle, figureMain));
1188
-
1189
- _DragHandle.get('__dragMove')();
1190
-
1191
- dragHandle.style.display = 'block';
1192
- }
1193
-
1194
- /**
1195
- * @param {HTMLElement} dragHandle Drag handle element
1196
- * @param {HTMLElement} figureMain Figure container element
1197
- */
1198
- #OnScrollDragHandler(dragHandle, figureMain) {
1199
- dragHandle.style.display = 'block';
1200
- dragHandle.style.left = figureMain.offsetLeft + (this.options.get('_rtl') ? dragHandle.offsetWidth : figureMain.offsetWidth - dragHandle.offsetWidth * 1.5) + 'px';
1201
- dragHandle.style.top = figureMain.offsetTop - dragHandle.offsetHeight + 'px';
1202
- }
1203
-
1204
- /**
1205
- * @param {MouseEvent} e Event object
1206
- */
1207
- #OnResizeContainer(e) {
1208
- e.stopPropagation();
1209
- e.preventDefault();
1210
-
1211
- const eventTarget = dom.query.getEventTarget(e);
1212
- const inst = _DragHandle.get('__figureInst');
1213
- const direction = (inst._resize_direction = eventTarget.classList[0]);
1214
- inst._resizeClientX = e.clientX;
1215
- inst._resizeClientY = e.clientY;
1216
- inst.editor.frameContext.get('_figure').main.style.float = /l/.test(direction) ? 'right' : /r/.test(direction) ? 'left' : 'none';
1217
- this.ui.enableBackWrapper(DIRECTION_CURSOR_MAP[direction]);
1218
-
1219
- const { w, h } = this.getSize(inst._element);
1220
- __resizing_p_wh = __resizing_p_ow = false;
1221
- __resizing_cw = __resizing_sw = 0;
1222
- if (!this.isVertical) {
1223
- const pw = !w || /auto|%$/.test(w);
1224
- const ph = !h || /auto|%$/.test(h);
1225
- if (DIR_DIAGONAL.includes(direction) && pw && ph) {
1226
- __resizing_p_wh = true;
1227
- } else if (DIR_W.includes(direction) && pw) {
1228
- __resizing_p_ow = true;
1229
- }
1230
-
1231
- if (__resizing_p_wh || __resizing_p_ow) {
1232
- const sizeTarget = inst._cover || inst._element;
1233
- __resizing_sw = sizeTarget.offsetWidth;
1234
- __resizing_cw = converter.getWidthInPercentage(sizeTarget, this.editor.frameContext.get('wysiwygFrame')) / 100;
1235
- }
1236
- }
1237
-
1238
- inst.__onContainerEvent = inst.eventManager.addGlobalEvent('mousemove', inst.__containerResizing);
1239
- inst.__offContainerEvent = inst.eventManager.addGlobalEvent('mouseup', inst.__containerResizingOff);
1240
- inst._displayResizeHandles(false);
1241
-
1242
- const _display = this.editor.frameContext.get('_figure').display;
1243
- _display.style.display = 'block';
1244
- dom.utils.changeTxt(_display, w + ' x ' + h);
1245
- }
1246
-
1247
- /**
1248
- * @description Handles the resizing of the figure container.
1249
- * @param {MouseEvent} e Mouse event.
1250
- */
1251
- #ContainerResizing(e) {
1252
- const direction = this._resize_direction;
1253
- const clientX = e.clientX;
1254
- const clientY = e.clientY;
1255
-
1256
- let resultW = this._element_w;
1257
- let resultH = this._element_h;
1258
-
1259
- const w = resultW + (/r/.test(direction) ? clientX - this._resizeClientX : this._resizeClientX - clientX);
1260
- const h = resultH + (/b/.test(direction) ? clientY - this._resizeClientY : this._resizeClientY - clientY);
1261
- const wh = (resultH / resultW) * w;
1262
- const resizeBorder = this.editor.frameContext.get('_figure').border;
1263
-
1264
- if (/t/.test(direction)) resizeBorder.style.top = resultH - (/h/.test(direction) ? h : wh) + 'px';
1265
- if (/l/.test(direction)) resizeBorder.style.left = resultW - w + 'px';
1266
-
1267
- if (/r|l/.test(direction)) {
1268
- resizeBorder.style.width = w + 'px';
1269
- resultW = w;
1270
- }
1271
-
1272
- if (/^(t|b)[^h]$/.test(direction)) {
1273
- resizeBorder.style.height = wh + 'px';
1274
- resultH = wh;
1275
- } else if (/^(t|b)h$/.test(direction)) {
1276
- resizeBorder.style.height = h + 'px';
1277
- resultH = h;
1278
- }
1279
-
1280
- this._resize_w = /** @type {number} */ (/h$/.test(direction) ? this._width : Math.round(resultW));
1281
- this._resize_h = /** @type {number} */ (/w$/.test(direction) ? this._height : Math.round(resultH));
1282
- const rw = __resizing_cw ? (this._resize_w / __resizing_sw) * __resizing_cw * 100 : this._resize_w;
1283
- dom.utils.changeTxt(this.editor.frameContext.get('_figure').display, __resizing_cw ? numbers.get(rw > 100 ? 100 : rw, 2).toFixed(2) + '%' : rw + ' x ' + this._resize_h);
1284
- }
1285
-
1286
- /**
1287
- * @description Finalizes the resizing process of the figure container.
1288
- */
1289
- #ContainerResizingOff() {
1290
- this._offResizeEvent();
1291
-
1292
- // set size
1293
- let w = this.isVertical ? this._resize_h : this._resize_w;
1294
- let h = this.isVertical ? this._resize_w : this._resize_h;
1295
- w = Math.round(w) || w;
1296
- h = Math.round(h) || h;
1297
-
1298
- if (!this.isVertical && !/%$/.test(w + '')) {
1299
- const limit =
1300
- this.editor.frameContext.get('wysiwygFrame').clientWidth -
1301
- numbers.get(this.editor.frameContext.get('wwComputedStyle').getPropertyValue('padding-left')) +
1302
- numbers.get(this.editor.frameContext.get('wwComputedStyle').getPropertyValue('padding-right')) -
1303
- 2;
1304
- if (numbers.get(w, 0) > limit) {
1305
- h = Math.round((h / w) * limit);
1306
- w = limit;
1307
- }
1308
- }
1309
-
1310
- if (__resizing_p_wh || __resizing_p_ow) {
1311
- const sizeTarget = this._cover || this._element;
1312
- w = (w / sizeTarget.offsetWidth) * __resizing_cw * 100;
1313
- const wp = numbers.get(w > 100 ? 100 : w, 2) + '%';
1314
- this._setPercentSize(wp, __resizing_p_ow ? this.getSize(this._element).h : '');
1315
- } else {
1316
- this._applySize(w, h, this._resize_direction);
1317
- if (this.isVertical) this.setTransform(this._element, w, h, 0);
1318
- }
1319
-
1320
- this.history.push(false);
1321
- this.component.select(this._element, this.kind);
1322
- }
1323
-
1324
- /**
1325
- * @description Cancels the resizing process when the escape key is pressed.
1326
- * @param {KeyboardEvent} e Keyboard event.
1327
- */
1328
- #ContainerResizingESC(e) {
1329
- if (!keyCodeMap.isEsc(e.code)) return;
1330
- this._offResizeEvent();
1331
- this.component.select(this._element, this.kind);
1332
- }
1333
-
1334
- /**
1335
- * @param { "none"|"left"|"center"|"right"} value align value
1336
- */
1337
- #SetMenuAlign(value) {
1338
- this.setAlign(this._element, value);
1339
- this.selectMenu_align.close();
1340
- this.component.select(this._element, this.kind);
1341
- }
1342
-
1343
- /**
1344
- * @param {"block"|"inline"} value Format style
1345
- */
1346
- #SetMenuAs(value) {
1347
- this.convertAsFormat(this._element, value);
1348
- this.selectMenu_as.close();
1349
- }
1350
-
1351
- /**
1352
- * @param {"auto"|number} value Resize value
1353
- */
1354
- #SetResize(value) {
1355
- if (value === 'auto') {
1356
- this.deleteTransform();
1357
- this._setAutoSize();
1358
- } else {
1359
- let dataY = this.getSize(this._element).h;
1360
- if (this.isVertical) {
1361
- const dataSize = (this._element.getAttribute('data-se-size') || ',').split(',');
1362
- if (dataSize[1]) dataY = dataSize[1];
1363
- }
1364
-
1365
- this.deleteTransform();
1366
- this._setPercentSize(value * 1, numbers.get(dataY, 0) === null || !/%$/.test(dataY) ? '' : dataY);
1367
- }
1368
-
1369
- this.selectMenu_resize.close();
1370
- this.component.select(this._element, this.kind);
1371
- }
1372
-
1373
- #OffFigureContainer() {
1374
- this.editor.frameContext.get('_figure').main.style.display = 'none';
1375
- this.editor._figureContainer = null;
1376
- }
1377
-
1378
- #OnClick_alignButton() {
1379
- this.selectMenu_align.open('', '[data-command="' + this.align + '"]');
1380
- }
1381
-
1382
- #OnClick_asButton() {
1383
- this.selectMenu_as.open('', '[data-command="' + this.as + '"]');
1384
- }
1385
-
1386
- #OnClick_resizeButton() {
1387
- const size = this.getSize(this._element);
1388
- const w = size.w;
1389
- const h = size.h;
1390
- let command = '';
1391
- if (this.autoRatio) {
1392
- if (h === this.autoRatio.default && /%$/.test(w)) {
1393
- const nw = numbers.get(w);
1394
- if (nw === 100) command = 'auto';
1395
- else command = 'resize_percent' + nw;
1396
- }
1397
- } else if (h === 'auto') {
1398
- if (w === 'auto') {
1399
- command = 'auto';
1400
- } else if (/%$/.test(w)) {
1401
- command = 'resize_percent' + numbers.get(w);
1402
- }
1403
- }
1404
-
1405
- this.selectMenu_resize.open('', '[data-command="' + command + '"]');
1406
- }
1407
-
1408
- /**
1409
- * @param {Node} element Target element
1410
- * @returns {{ r: *, x: *, y: * }} Rotation value
1411
- */
1412
- #GetRotateValue(element) {
1413
- const transform = /** @type {HTMLElement} */ (element).style.transform;
1414
- if (!transform) return { r: 0, x: '', y: '' };
1415
- return {
1416
- r: Number((transform.match(/rotate\(([-0-9]+)deg\)/) || [])[1] || 0),
1417
- x: (transform.match(/rotateX\(([-0-9]+)deg\)/) || [])[1] || '',
1418
- y: (transform.match(/rotateY\(([-0-9]+)deg\)/) || [])[1] || ''
1419
- };
1420
- }
1421
- }
1422
-
1423
- function CreateAlign(inst, button) {
1424
- if (!button) return null;
1425
-
1426
- const icons = [inst._alignIcons.none, inst._alignIcons.left, inst._alignIcons.center, inst._alignIcons.right];
1427
- const langs = [inst.lang.basic, inst.lang.left, inst.lang.center, inst.lang.right];
1428
- const commands = ['none', 'left', 'center', 'right'];
1429
- const html = [];
1430
- const items = [];
1431
- for (let i = 0; i < commands.length; i++) {
1432
- html.push(/*html*/ `
1433
- <button type="button" class="se-btn-list se-tooltip" data-command="${commands[i]}" data-type="selectMenu" >
1434
- ${icons[i]}
1435
- <span class="se-tooltip-inner">
1436
- <span class="se-tooltip-text">${langs[i]}</span>
1437
- </span>
1438
- </button>`);
1439
- items.push(commands[i]);
1440
- }
1441
-
1442
- return { html: html, items: items };
1443
- }
1444
-
1445
- function CreateAs(inst, button) {
1446
- if (!button) return null;
1447
-
1448
- const icons = [inst.icons.as_block, inst.icons.as_inline];
1449
- const langs = [inst.lang.asBlock, inst.lang.asInline];
1450
- const commands = ['block', 'inline'];
1451
- const html = [];
1452
- const items = [];
1453
- for (let i = 0; i < commands.length; i++) {
1454
- html.push(/*html*/ `
1455
- <button type="button" class="se-btn-list se-tooltip" data-command="${commands[i]}" data-type="selectMenu" >
1456
- ${icons[i]}
1457
- <span class="se-tooltip-inner">
1458
- <span class="se-tooltip-text">${langs[i]}</span>
1459
- </span>
1460
- </button>`);
1461
- items.push(commands[i]);
1462
- }
1463
-
1464
- return { html: html, items: items };
1465
- }
1466
-
1467
- function CreateResize(editor, button) {
1468
- if (!button) return null;
1469
-
1470
- const items = button.getAttribute('data-value').split(',');
1471
- const html = [];
1472
- for (let i = 0, n, c, v, l; i < items.length; i++) {
1473
- v = items[i];
1474
- n = numbers.is(v);
1475
- c = n ? 'resize_percent' + v : 'auto';
1476
- l = n ? v + '%' : editor.lang.autoSize;
1477
- html.push('<button type="button" class="se-btn-list" data-command="' + c + '" data-value="' + v + '"><span>' + l + '</span></button>');
1478
- }
1479
-
1480
- return { html: html, items: items };
1481
- }
1482
-
1483
- function CreateHTML_resizeDot() {
1484
- const html = /*html*/ `
1485
- <div class="se-resize-dot">
1486
- <span class="tl"></span>
1487
- <span class="tr"></span>
1488
- <span class="bl"></span>
1489
- <span class="br"></span>
1490
- <span class="lw"></span>
1491
- <span class="th"></span>
1492
- <span class="rw"></span>
1493
- <span class="bh"></span>
1494
- <div class="se-resize-display"></div>
1495
- </div>`;
1496
-
1497
- return dom.utils.createElement('DIV', { class: 'se-controller se-resizing-container', style: 'display: none;' }, html);
1498
- }
1499
-
1500
- function GET_CONTROLLER_BUTTONS(group) {
1501
- const g = group.split('_');
1502
- const command = g[0];
1503
- const value = g[1];
1504
- let c, v, l, t, i;
1505
-
1506
- switch (command) {
1507
- case 'resize':
1508
- c = 'onresize';
1509
- v = value;
1510
- l = 'resize';
1511
- i = 'resize';
1512
- break;
1513
- case 'auto':
1514
- c = 'auto';
1515
- l = 'autoSize';
1516
- i = 'auto_size';
1517
- break;
1518
- case 'rotate':
1519
- c = 'rotate';
1520
- v = value === 'l' ? -90 : value === 'r' ? 90 : numbers.get(value);
1521
- l = v < 0 ? 'rotateLeft' : 'rotateRight';
1522
- i = v < 0 ? 'rotate_left' : 'rotate_right';
1523
- break;
1524
- case 'mirror':
1525
- c = 'mirror';
1526
- v = value;
1527
- l = value === 'h' ? 'mirrorHorizontal' : 'mirrorVertical';
1528
- i = value === 'h' ? 'mirror_horizontal' : 'mirror_vertical';
1529
- break;
1530
- case 'align':
1531
- c = 'onalign';
1532
- l = 'align';
1533
- i = 'align_justify';
1534
- break;
1535
- case 'caption':
1536
- c = 'caption';
1537
- l = 'caption';
1538
- i = 'caption';
1539
- break;
1540
- case 'revert':
1541
- c = 'revert';
1542
- l = 'revert';
1543
- i = 'revert';
1544
- break;
1545
- case 'edit':
1546
- c = 'edit';
1547
- l = 'edit';
1548
- i = 'edit';
1549
- break;
1550
- case 'as':
1551
- c = 'onas';
1552
- l = 'blockStyle';
1553
- i = 'as_block';
1554
- break;
1555
- case 'copy':
1556
- c = 'copy';
1557
- l = 'copy';
1558
- i = 'copy';
1559
- break;
1560
- case 'remove':
1561
- c = 'remove';
1562
- l = 'remove';
1563
- i = 'delete';
1564
- break;
1565
- }
1566
-
1567
- if (!c) return null;
1568
-
1569
- return {
1570
- c: c,
1571
- v: v,
1572
- l: l,
1573
- t: t,
1574
- i: i
1575
- };
1576
- }
1577
-
1578
- function CreateHTML_controller(inst, controls) {
1579
- let html = null;
1580
-
1581
- if (controls?.length > 0) {
1582
- const { lang, icons } = inst;
1583
- html = '<div class="se-arrow se-arrow-up"></div>';
1584
- for (let i = 0, group; i < controls.length; i++) {
1585
- group = controls[i];
1586
- html += '<div class="se-btn-group">';
1587
- for (let j = 0, len = group.length, m; j < len; j++) {
1588
- m = group[j];
1589
-
1590
- if (typeof m?.action === 'function') {
1591
- const g = m;
1592
- m = {
1593
- c: `__c__${g.command}`,
1594
- v: g.value || '',
1595
- l: g.title,
1596
- i: g.icon
1597
- };
1598
- inst._action[m.c] = g.action;
1599
- } else {
1600
- m = GET_CONTROLLER_BUTTONS(m);
1601
- if (!m) continue;
1602
- }
1603
-
1604
- html += /*html*/ `
1605
- <button type="button" data-command="${m.c}" data-value="${m.v}" class="se-btn se-tooltip">
1606
- ${icons[m.i] || m.i}
1607
- <span class="se-tooltip-inner"><span class="se-tooltip-text">${lang[m.l] || m.l}</span></span>
1608
- </button>`;
1609
- }
1610
- html += '</div>';
1611
- }
1612
- }
1613
-
1614
- return dom.utils.createElement('DIV', { class: 'se-controller se-controller-resizing' + (!html ? ' se-empty-controller' : '') }, html);
1615
- }
1616
-
1617
- export default Figure;
1
+ import EditorInjector from '../editorInjector';
2
+ import { Controller, SelectMenu, _DragHandle } from '../modules';
3
+ import { dom, numbers, env, converter, keyCodeMap } from '../helper';
4
+
5
+ const { ON_OVER_COMPONENT } = env;
6
+ const DIRECTION_CURSOR_MAP = { tl: 'nwse-resize', tr: 'nesw-resize', bl: 'nesw-resize', br: 'nwse-resize', lw: 'ew-resize', th: 'ns-resize', rw: 'ew-resize', bh: 'ns-resize' };
7
+ const DIR_DIAGONAL = 'tl|bl|tr|br';
8
+ const DIR_W = 'lw|rw';
9
+ let __resizing_p_wh = false;
10
+ let __resizing_p_ow = false;
11
+ let __resizing_cw = 0;
12
+ let __resizing_sw = 0;
13
+
14
+ /**
15
+ * @typedef {Object} FigureParams
16
+ * @property {string} [sizeUnit="px"] Size unit
17
+ * @property {{ current: string, default: string }} [autoRatio=null] Auto ratio { current: '00%', default: '00%' }
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} FigureInfo
22
+ * @property {HTMLElement} target Target element (img, iframe, video, audio, table, etc.)
23
+ * @property {HTMLElement} container Container element (div.se-component|span.se-component.se-inline-component)
24
+ * @property {?HTMLElement} cover Cover element (FIGURE|null)
25
+ * @property {?HTMLElement} inlineCover Inline cover element (span.se-inline-component)
26
+ * @property {?HTMLElement} caption Caption element (FIGCAPTION)
27
+ */
28
+
29
+ /**
30
+ * @typedef {Object} FigureTargetInfo
31
+ * @property {HTMLElement} container Container element (div.se-component|span.se-component.se-inline-component)
32
+ * @property {?HTMLElement=} cover Cover element (FIGURE|null)
33
+ * @property {?HTMLElement=} caption Caption element (FIGCAPTION)
34
+ * @property {string} [align] - Alignment of the element.
35
+ * @property {{w:number, h:number}} [ratio] - The aspect ratio of the element.
36
+ * @property {string|number} [w] - Width of the element.
37
+ * @property {string|number} [h] - Height of the element.
38
+ * @property {number} [t] - Top position.
39
+ * @property {number} [l] - Left position.
40
+ * @property {string|number} width - Width, can be a number or 'auto'.
41
+ * @property {string|number} height - Height, can be a number or 'auto'.
42
+ * @property {number} [originWidth] - Original width from `naturalWidth` or `offsetWidth`.
43
+ * @property {number} [originHeight] - Original height from `naturalHeight` or `offsetHeight`.
44
+ */
45
+
46
+ /**
47
+ * @typedef {Array<Array<
48
+ * string |
49
+ * {
50
+ * action: (element: Node, value: string, target: Node) => void,
51
+ * command: string,
52
+ * value: string,
53
+ * title: string,
54
+ * icon: string
55
+ * }
56
+ * >>} FigureControls
57
+ * "mirror". "rotate", "caption", "revert", "edit", "copy", "remove", "as", "resize_auto,[number]"
58
+ */
59
+
60
+ /**
61
+ * @class
62
+ * @description Controller module class
63
+ */
64
+ class Figure extends EditorInjector {
65
+ /**
66
+ * @constructor
67
+ * @param {*} inst The instance object that called the constructor.
68
+ * @param {FigureControls} controls Controller button array
69
+ * @param {FigureParams} params Figure options
70
+ */
71
+ constructor(inst, controls, params) {
72
+ super(inst.editor);
73
+ this.kind = inst.constructor.key || inst.constructor.name;
74
+ this._alignIcons = {
75
+ none: this.icons.format_float_none,
76
+ left: this.icons.format_float_left,
77
+ right: this.icons.format_float_right,
78
+ center: this.icons.format_float_center
79
+ };
80
+
81
+ // modules
82
+ /** @type {Object<string, *>} */
83
+ this._action = {};
84
+ const controllerEl = CreateHTML_controller(this, controls || []);
85
+ this.controller = new Controller(this, controllerEl, { position: 'bottom', disabled: true }, this.kind);
86
+ // align selectmenu
87
+ this.alignButton = controllerEl.querySelector('[data-command="onalign"]');
88
+ const alignMenus = CreateAlign(this, this.alignButton);
89
+ if (alignMenus) {
90
+ this.selectMenu_align = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
91
+ this.selectMenu_align.on(this.alignButton, this.#SetMenuAlign.bind(this), { class: 'se-figure-select-list' });
92
+ this.selectMenu_align.create(alignMenus.items, alignMenus.html);
93
+ }
94
+ // as [block, inline] selectmenu
95
+ this.asButton = controllerEl.querySelector('[data-command="onas"]');
96
+ const asMenus = CreateAs(this, this.asButton);
97
+ if (asMenus) {
98
+ this.selectMenu_as = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
99
+ this.selectMenu_as.on(this.asButton, this.#SetMenuAs.bind(this), { class: 'se-figure-select-list' });
100
+ this.selectMenu_as.create(asMenus.items, asMenus.html);
101
+ }
102
+ // resize selectmenu
103
+ this.resizeButton = controllerEl.querySelector('[data-command="onresize"]');
104
+ const resizeMenus = CreateResize(this, this.resizeButton);
105
+ if (resizeMenus) {
106
+ this.selectMenu_resize = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr' });
107
+ this.selectMenu_resize.on(this.resizeButton, this.#SetResize.bind(this));
108
+ this.selectMenu_resize.create(resizeMenus.items, resizeMenus.html);
109
+ }
110
+
111
+ // members
112
+ this.inst = inst;
113
+ this.sizeUnit = params.sizeUnit || 'px';
114
+ this.autoRatio = params.autoRatio;
115
+ this.isVertical = false;
116
+ this.percentageButtons = controllerEl.querySelectorAll('[data-command="resize_percent"]');
117
+ this.captionButton = controllerEl.querySelector('[data-command="caption"]');
118
+ this.align = 'none';
119
+ this.as = 'block';
120
+ this._element = null;
121
+ this._cover = null;
122
+ this._inlineCover = null;
123
+ this._container = null;
124
+ this._caption = null;
125
+ this._width = '';
126
+ this._height = '';
127
+ this._resize_w = 0;
128
+ this._resize_h = 0;
129
+ this._element_w = 0;
130
+ this._element_h = 0;
131
+ this._element_l = 0;
132
+ this._element_t = 0;
133
+ this._resizeClientX = 0;
134
+ this._resizeClientY = 0;
135
+ this._resize_direction = '';
136
+ this._floatClassStr = '__se__float-none|__se__float-left|__se__float-center|__se__float-right';
137
+ this.__preventSizechange = false;
138
+ this.__revertSize = { w: '', h: '' };
139
+ /** @type {{left?: number, top?: number}} */
140
+ this.__offset = {};
141
+ this.__offContainer = this.#OffFigureContainer.bind(this);
142
+ this.__containerResizing = this.#ContainerResizing.bind(this);
143
+ this.__containerResizingOff = this.#ContainerResizingOff.bind(this);
144
+ this.__containerResizingESC = this.#ContainerResizingESC.bind(this);
145
+ this.__onContainerEvent = null;
146
+ this.__offContainerEvent = null;
147
+ this.__onResizeESCEvent = null;
148
+ this.__fileManagerInfo = false;
149
+
150
+ // init
151
+ this.eventManager.addEvent(this.alignButton, 'click', this.#OnClick_alignButton.bind(this));
152
+ this.eventManager.addEvent(this.asButton, 'click', this.#OnClick_asButton.bind(this));
153
+ this.eventManager.addEvent(this.resizeButton, 'click', this.#OnClick_resizeButton.bind(this));
154
+ this.editor.applyFrameRoots((e) => {
155
+ if (!e.get('wrapper').querySelector('.se-controller.se-resizing-container')) {
156
+ // resizing
157
+ const main = CreateHTML_resizeDot();
158
+ const handles = main.querySelectorAll('.se-resize-dot > span');
159
+ e.set('_figure', {
160
+ main: main,
161
+ border: main.querySelector('.se-resize-dot'),
162
+ display: main.querySelector('.se-resize-display'),
163
+ handles: handles
164
+ });
165
+ e.get('wrapper').appendChild(main);
166
+ this.eventManager.addEvent(handles, 'mousedown', this.#OnResizeContainer.bind(this));
167
+ }
168
+ });
169
+ }
170
+
171
+ /**
172
+ * @description Create a container for the resizing component and insert the element.
173
+ * @param {Node} element Target element
174
+ * @param {string=} className Class name of container (fixed: se-component)
175
+ * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
176
+ */
177
+ static CreateContainer(element, className) {
178
+ dom.utils.createElement('DIV', { class: 'se-component' + (className ? ' ' + className : '') }, dom.utils.createElement('FIGURE', null, element));
179
+ return Figure.GetContainer(element);
180
+ }
181
+
182
+ /**
183
+ * @description Create a container for the inline resizing component and insert the element.
184
+ * @param {Node} element Target element
185
+ * @param {string} [className] Class name of container (fixed: se-component se-inline-component)
186
+ * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
187
+ */
188
+ static CreateInlineContainer(element, className) {
189
+ dom.utils.createElement('SPAN', { class: 'se-component se-inline-component' + (className ? ' ' + className : '') }, element);
190
+ return Figure.GetContainer(element);
191
+ }
192
+
193
+ /**
194
+ * @description Return HTML string of caption(FIGCAPTION) element
195
+ * @param {Node} cover Cover element(FIGURE). "CreateContainer().cover"
196
+ * @returns {HTMLElement} caption element
197
+ */
198
+ static CreateCaption(cover, text) {
199
+ const caption = dom.utils.createElement('FIGCAPTION', null, '<div>' + text + '</div>');
200
+ cover.appendChild(caption);
201
+ return caption;
202
+ }
203
+
204
+ /**
205
+ * @description Get the element's container(.se-component) info.
206
+ * @param {Node} element Target element
207
+ * @returns {FigureInfo} {target, container, cover, inlineCover, caption}
208
+ */
209
+ static GetContainer(element) {
210
+ const cover = dom.query.getParentElement(element, 'FIGURE', 2);
211
+ const inlineCover = dom.query.getParentElement(element, 'SPAN', 2);
212
+ return {
213
+ target: /** @type {HTMLElement} */ (element),
214
+ container: /** @type {HTMLElement} */ (dom.query.getParentElement(element, Figure.is, 2) || cover),
215
+ cover: /** @type {HTMLElement} */ (cover),
216
+ inlineCover: dom.utils.hasClass(inlineCover, 'se-inline-component') ? /** @type {HTMLElement} */ (inlineCover) : null,
217
+ caption: /** @type {HTMLElement} */ (dom.query.getEdgeChild(element.parentElement, 'FIGCAPTION', false))
218
+ };
219
+ }
220
+
221
+ /**
222
+ * @description Ratio calculation
223
+ * @param {string|number} w Width size
224
+ * @param {string|number} h Height size
225
+ * @param {?string=} [defaultSizeUnit="px"] Default size unit (default: "px")
226
+ * @return {{w: number, h: number}}
227
+ */
228
+ static GetRatio(w, h, defaultSizeUnit) {
229
+ let rw = 1,
230
+ rh = 1;
231
+ if (/\d+/.test(w + '') && /\d+/.test(h + '')) {
232
+ const xUnit = (!numbers.is(w) && String(w).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
233
+ const yUnit = (!numbers.is(h) && String(h).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
234
+ if (xUnit === yUnit) {
235
+ const w_number = numbers.get(w, 4);
236
+ const h_number = numbers.get(h, 4);
237
+ rw = w_number / h_number;
238
+ rh = h_number / w_number;
239
+ }
240
+ }
241
+
242
+ return {
243
+ w: numbers.get(rw, 4),
244
+ h: numbers.get(rh, 4)
245
+ };
246
+ }
247
+
248
+ /**
249
+ * @description Ratio calculation
250
+ * @param {string|number} w Width size
251
+ * @param {string|number} h Height size
252
+ * @param {string} defaultSizeUnit Default size unit (default: "px")
253
+ * @param {{w: number, h: number}} ratio Ratio size (Figure.GetRatio)
254
+ * @return {{w: string|number, h: string|number}}
255
+ */
256
+ static CalcRatio(w, h, defaultSizeUnit, ratio) {
257
+ if (/\d+/.test(w + '') && /\d+/.test(h + '')) {
258
+ const xUnit = (!numbers.is(w) && String(w).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
259
+ const yUnit = (!numbers.is(h) && String(h).replace(/\d+|\./g, '')) || defaultSizeUnit || 'px';
260
+ if (xUnit === yUnit) {
261
+ const dec = xUnit === '%' ? 2 : 0;
262
+ const ow = w;
263
+ const oh = h;
264
+ h = numbers.get(ratio.h * numbers.get(ow, dec), dec) + yUnit;
265
+ w = numbers.get(ratio.w * numbers.get(oh, dec), dec) + xUnit;
266
+ }
267
+ }
268
+
269
+ return {
270
+ w: w,
271
+ h: h
272
+ };
273
+ }
274
+
275
+ /**
276
+ * @description It is judged whether it is the component[img, iframe, video, audio, table] cover(class="se-component") and table, hr
277
+ * @param {Node} element Target element
278
+ * @returns {boolean}
279
+ */
280
+ static is(element) {
281
+ return dom.utils.hasClass(element, 'se-component') || /^(HR)$/.test(element?.nodeName);
282
+ }
283
+
284
+ /**
285
+ * @description Close the figure's controller
286
+ */
287
+ close() {
288
+ this.editor._preventBlur = false;
289
+ dom.utils.removeClass(this._cover, 'se-figure-selected');
290
+ this.controller.close();
291
+ this.component._removeDragEvent();
292
+ }
293
+
294
+ /**
295
+ * @description Open the figure's controller
296
+ * @param {Node} targetNode Target element
297
+ * @param {Object} params params
298
+ * @param {boolean} [params.nonResizing=false] Do not display the resizing button
299
+ * @param {boolean} [params.nonSizeInfo=false] Do not display the size information
300
+ * @param {boolean} [params.nonBorder=false] Do not display the selected style line
301
+ * @param {boolean} [params.figureTarget=false] If true, the target is a figure element
302
+ * @param {boolean} [params.__fileManagerInfo=false] If true, the file manager is called
303
+ * @returns {FigureTargetInfo|undefined} figure target info
304
+ */
305
+ open(targetNode, { nonResizing, nonSizeInfo, nonBorder, figureTarget, __fileManagerInfo }) {
306
+ if (!targetNode) {
307
+ console.warn('[SUNEDITOR.modules.Figure.open] The "targetNode" is null.');
308
+ return;
309
+ }
310
+
311
+ if (_DragHandle.get('__overInfo') === ON_OVER_COMPONENT) {
312
+ nonBorder = true;
313
+ }
314
+
315
+ const figureInfo = Figure.GetContainer(targetNode);
316
+ const target = figureInfo.target;
317
+ let exceptionFormat = false;
318
+ if (!figureInfo.container) {
319
+ if (!this.options.get('strictMode').formatFilter) {
320
+ figureInfo.container = target;
321
+ figureInfo.cover = target;
322
+ exceptionFormat = true;
323
+ } else {
324
+ return {
325
+ container: null,
326
+ cover: null,
327
+ width: target.style.width || /** @type {HTMLImageElement} */ (target).width || '',
328
+ height: target.style.height || /** @type {HTMLImageElement} */ (target).height || ''
329
+ };
330
+ }
331
+ }
332
+
333
+ _DragHandle.set('__figureInst', this);
334
+
335
+ this._setFigureInfo(figureInfo);
336
+
337
+ const sizeTarget = /** @type {HTMLElement} */ (figureTarget ? this._cover || this._container || target : target);
338
+ const w = sizeTarget.offsetWidth || null;
339
+ const h = sizeTarget.offsetHeight || null;
340
+ const { top, left, scrollX, scrollY } = this.offset.getLocal(sizeTarget);
341
+
342
+ const dataSize = (target.getAttribute('data-se-size') || '').split(',');
343
+ const ratio = Figure.GetRatio(dataSize[0] || numbers.get(target.style.width, 2) || w, dataSize[1] || numbers.get(target.style.height, 2) || h, this.sizeUnit);
344
+ const targetInfo = {
345
+ container: figureInfo.container,
346
+ cover: figureInfo.cover,
347
+ caption: figureInfo.caption,
348
+ align: this.align,
349
+ ratio: ratio,
350
+ w: w || '',
351
+ h: h || '',
352
+ t: top,
353
+ l: left,
354
+ width: dataSize[0] || 'auto',
355
+ height: dataSize[1] || 'auto',
356
+ originWidth: /** @type {HTMLImageElement} */ (target).naturalWidth || target.offsetWidth,
357
+ originHeight: /** @type {HTMLImageElement} */ (target).naturalHeight || target.offsetHeight
358
+ };
359
+
360
+ this._width = targetInfo.width;
361
+ this._height = targetInfo.height;
362
+ if (__fileManagerInfo || this.__fileManagerInfo) return targetInfo;
363
+
364
+ const _figure = this.editor.frameContext.get('_figure');
365
+ this.editor._figureContainer = _figure.main;
366
+ _figure.main.style.top = top + 'px';
367
+ _figure.main.style.left = left + 'px';
368
+ _figure.main.style.width = (this.isVertical ? h : w) + 'px';
369
+ _figure.main.style.height = (this.isVertical ? w : h) + 'px';
370
+ _figure.border.style.top = '0px';
371
+ _figure.border.style.left = '0px';
372
+ _figure.border.style.width = (this.isVertical ? h : w) + 'px';
373
+ _figure.border.style.height = (this.isVertical ? w : h) + 'px';
374
+
375
+ this.__offset = { left: left + scrollX, top: top + scrollY };
376
+ this.editor.opendControllers.push({
377
+ position: 'none',
378
+ form: _figure.main,
379
+ target: sizeTarget,
380
+ inst: this,
381
+ notInCarrier: true
382
+ });
383
+
384
+ // percentage active
385
+ const value = /%$/.test(target.style.width) && /%$/.test(figureInfo.container.style.width) ? numbers.get(figureInfo.container.style.width, 0) / 100 + '' : '';
386
+ for (let i = 0, len = this.percentageButtons.length; i < len; i++) {
387
+ if (this.percentageButtons[i].getAttribute('data-value') === value) {
388
+ dom.utils.addClass(this.percentageButtons[i], 'active');
389
+ } else {
390
+ dom.utils.removeClass(this.percentageButtons[i], 'active');
391
+ }
392
+ }
393
+
394
+ // caption active
395
+ if (this.captionButton) {
396
+ if (figureInfo.caption) {
397
+ dom.utils.addClass(this.captionButton, 'active');
398
+ } else {
399
+ dom.utils.removeClass(this.captionButton, 'active');
400
+ }
401
+ }
402
+
403
+ _figure.display.style.display = nonSizeInfo || this._inlineCover ? 'none' : '';
404
+ _figure.border.style.display = nonBorder ? 'none' : '';
405
+ _figure.main.style.display = 'block';
406
+
407
+ if (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
408
+ // align button
409
+ this._setAlignIcon();
410
+ // as button
411
+ this._setAsIcon();
412
+ this.ui._visibleControllers(true, true);
413
+ // size
414
+ const size = this.getSize(target);
415
+ dom.utils.changeTxt(_figure.display, this.lang[this.align === 'none' ? 'basic' : this.align] + ' (' + size.w + ', ' + size.h + ')');
416
+ this._displayResizeHandles(!nonResizing);
417
+ // rotate, aption, align, onresize - display;
418
+ const transformButtons = this.controller.form.querySelectorAll(
419
+ '[data-command="rotate"][data-value="90"], [data-command="rotate"][data-value="-90"], [data-command="caption"], [data-command="onalign"], [data-command="onresize"]'
420
+ );
421
+ const display = this._inlineCover || exceptionFormat ? 'none' : '';
422
+ transformButtons.forEach((button) => {
423
+ /** @type {HTMLButtonElement} */ (button).style.display = display;
424
+ });
425
+ // onas
426
+ const onas = this.controller.form.querySelector('[data-command="onas"]');
427
+ if (onas) {
428
+ /** @type {HTMLButtonElement} */ (onas).style.display = exceptionFormat ? 'none' : '';
429
+ }
430
+ // selecte
431
+ dom.utils.removeClass(this._cover, 'se-figure-over-selected');
432
+ this.controller.open(_figure.main, null, { initMethod: this.__offContainer, isWWTarget: false, addOffset: null });
433
+ this._w.setTimeout(() => _DragHandle.set('__overInfo', false), 0);
434
+ } else {
435
+ dom.utils.addClass(this._cover, 'se-figure-over-selected');
436
+ }
437
+
438
+ // set members
439
+ dom.utils.addClass(this._cover, 'se-figure-selected');
440
+ this._element_w = this._resize_w = w;
441
+ this._element_h = this._resize_h = h;
442
+ this._element_l = left;
443
+ this._element_t = top;
444
+
445
+ // drag
446
+ if (!this._inlineCover && (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT || dom.utils.hasClass(figureInfo.container, 'se-input-component'))) {
447
+ this._setDragEvent(_figure.main);
448
+ }
449
+
450
+ return targetInfo;
451
+ }
452
+
453
+ /**
454
+ * @description Hide the controller
455
+ */
456
+ controllerHide() {
457
+ this.controller.hide();
458
+ }
459
+
460
+ /**
461
+ * @description Hide the controller
462
+ */
463
+ controllerShow() {
464
+ this.controller.show();
465
+ }
466
+
467
+ /**
468
+ * @description Open the figure's controller
469
+ * @param {Node} target Target element
470
+ * @param {Object} [params={}] params
471
+ * @param {boolean=} params.isWWTarget If the controller is in the WYSIWYG area, set it to true.
472
+ * @param {() => void=} params.initMethod Method to be called when the controller is closed.
473
+ * @param {boolean=} params.disabled If true, the controller is disabled.
474
+ * @param {{left: number, top: number}=} params.addOffset Additional offset values
475
+ */
476
+ controllerOpen(target, params) {
477
+ this._element = /** @type {HTMLElement} */ (target);
478
+ this.controller.open(target, null, params);
479
+ }
480
+
481
+ /**
482
+ * @description Set the element's container size
483
+ * @param {string|number} w Width size
484
+ * @param {string|number} h Height size
485
+ */
486
+ setSize(w, h) {
487
+ if (/%$/.test(w + '')) {
488
+ this._setPercentSize(w, h);
489
+ } else if ((!w || w === 'auto') && (!h || h === 'auto')) {
490
+ if (this.autoRatio) this._setPercentSize(100, this.autoRatio.default || this.autoRatio.current);
491
+ else this._setAutoSize();
492
+ } else {
493
+ this._applySize(w, h, '');
494
+ }
495
+ }
496
+
497
+ /**
498
+ * @description Gets the Figure size
499
+ * @param {?Node=} targetNode Target element, default is the current element
500
+ * @returns {{w: string, h: string}}
501
+ */
502
+ getSize(targetNode) {
503
+ if (!targetNode) targetNode = this._element;
504
+ if (!targetNode) return { w: '', h: '' };
505
+
506
+ const figure = Figure.GetContainer(targetNode);
507
+ const target = figure.target;
508
+ if (!figure.container) {
509
+ // exceptionFormat
510
+ if (!this.options.get('strictMode').formatFilter) {
511
+ return {
512
+ w: target.style.width || 'auto',
513
+ h: target.style.height || 'auto'
514
+ };
515
+ }
516
+ return {
517
+ w: '',
518
+ h: target.style.height
519
+ };
520
+ }
521
+
522
+ const w = !/%$/.test(target.style.width) ? target.style.width : ((figure.container && numbers.get(figure.container.style.width, 2)) || 100) + '%';
523
+ const h = figure.inlineCover
524
+ ? figure.inlineCover.style.height || /** @type {HTMLElement} */ (targetNode).style.height || String(/** @type {HTMLImageElement} */ (targetNode).height || '')
525
+ : numbers.get(figure.cover.style.paddingBottom, 0) > 0 && !this.isVertical
526
+ ? figure.cover.style.height
527
+ : !/%$/.test(target.style.height) || !/%$/.test(target.style.width)
528
+ ? target.style.height
529
+ : ((figure.container && numbers.get(figure.container.style.height, 2)) || 100) + '%';
530
+ return {
531
+ w: w || 'auto',
532
+ h: h || 'auto'
533
+ };
534
+ }
535
+
536
+ /**
537
+ * @description Align the container.
538
+ * @param {?Node} targetNode Target element
539
+ * @param {string} align "none"|"left"|"center"|"right"
540
+ */
541
+ setAlign(targetNode, align) {
542
+ if (!targetNode) targetNode = this._element;
543
+ this.align = align = align || 'none';
544
+
545
+ const figure = Figure.GetContainer(targetNode);
546
+ if (!figure.cover) return;
547
+
548
+ const target = figure.target;
549
+ const container = figure.container;
550
+ const cover = figure.cover;
551
+ if (/%$/.test(target.style.width) && align === 'center' && !this.component.isInline(container)) {
552
+ container.style.minWidth = '100%';
553
+ cover.style.width = container.style.width;
554
+ } else {
555
+ container.style.minWidth = '';
556
+ cover.style.width = this.isVertical ? target.style.height || target.offsetHeight + 'px' : !target.style.width || target.style.width === 'auto' ? '' : target.style.width || '100%';
557
+ }
558
+
559
+ if (!dom.utils.hasClass(container, '__se__float-' + align)) {
560
+ dom.utils.removeClass(container, this._floatClassStr);
561
+ dom.utils.addClass(container, '__se__float-' + align);
562
+ }
563
+
564
+ if (this.autoRatio) {
565
+ const { w, h } = this.getSize(this._element);
566
+ this.__setCoverPaddingBottom(w, h);
567
+ }
568
+
569
+ this._setAlignIcon();
570
+ }
571
+
572
+ /**
573
+ * @description As style[block, inline] the component
574
+ * @param {?Node} targetNode Target element
575
+ * @param {"block"|"inline"} formatStyle Format style
576
+ * @returns {HTMLElement} New target element after conversion
577
+ */
578
+ convertAsFormat(targetNode, formatStyle) {
579
+ if (!targetNode) targetNode = this._element;
580
+ this.as = formatStyle || 'block';
581
+ const { container, inlineCover, target } = Figure.GetContainer(targetNode);
582
+ const { w, h } = this.getSize(target);
583
+
584
+ const newTarget = /** @type {HTMLElement} */ (target.cloneNode(false));
585
+ newTarget.style.width = '';
586
+ newTarget.style.height = '';
587
+ newTarget.removeAttribute('width');
588
+ newTarget.removeAttribute('height');
589
+
590
+ switch (formatStyle) {
591
+ case 'inline': {
592
+ if (inlineCover) break;
593
+ this.component.deselect();
594
+
595
+ const next = container.nextElementSibling;
596
+ const parent = container.parentElement;
597
+
598
+ const figure = Figure.CreateInlineContainer(newTarget);
599
+ dom.utils.addClass(
600
+ figure.container,
601
+ container.className
602
+ .split(' ')
603
+ .filter((v) => v !== 'se-figure-selected' && v !== 'se-component-selected')
604
+ .join('|')
605
+ );
606
+
607
+ this._asFormatChange(figure, w, h);
608
+
609
+ const line = dom.utils.createElement(this.options.get('defaultLine'), null, figure.container);
610
+ parent.insertBefore(line, next);
611
+ dom.utils.removeItem(container);
612
+
613
+ break;
614
+ }
615
+ case 'block': {
616
+ if (!inlineCover) break;
617
+ this.component.deselect();
618
+
619
+ this.selection.setRange(container, 0, container, 1);
620
+ const r = this.html.remove();
621
+ const s = this.nodeTransform.split(r.container, r.offset, 0);
622
+
623
+ if (s?.previousElementSibling && dom.check.isZeroWidth(s.previousElementSibling)) {
624
+ dom.utils.removeItem(s.previousElementSibling);
625
+ }
626
+
627
+ const figure = Figure.CreateContainer(newTarget);
628
+ dom.utils.addClass(
629
+ figure.container,
630
+ container.className
631
+ .split(' ')
632
+ .filter((v) => v !== 'se-inline-component' && v !== 'se-figure-selected' && v !== 'se-component-selected')
633
+ .join('|')
634
+ );
635
+
636
+ this._asFormatChange(figure, w, h);
637
+
638
+ (s || r.container).parentElement.insertBefore(figure.container, s);
639
+
640
+ break;
641
+ }
642
+ }
643
+
644
+ return newTarget;
645
+ }
646
+
647
+ /**
648
+ * @private
649
+ * @description Handles format conversion (block/inline) for the figure component and applies size changes.
650
+ * @param {FigureInfo} figureinfo {target, container, cover, inlineCover, caption}
651
+ * @param {string|number} w Width value.
652
+ * @param {string|number} h Height value.
653
+ */
654
+ _asFormatChange(figureinfo, w, h) {
655
+ const kind = this.kind;
656
+ figureinfo.target.onload = () => this.component.select(figureinfo.target, kind);
657
+
658
+ this._setFigureInfo(figureinfo);
659
+
660
+ if (figureinfo.inlineCover) {
661
+ this.setAlign(figureinfo.target, 'none');
662
+ this.deleteTransform();
663
+ }
664
+
665
+ this.setSize(w, h);
666
+ }
667
+
668
+ /**
669
+ * @description Controller button action
670
+ * @param {HTMLButtonElement} target Target button element
671
+ * @returns
672
+ */
673
+ controllerAction(target) {
674
+ const command = target.getAttribute('data-command');
675
+ const value = target.getAttribute('data-value');
676
+ const type = target.getAttribute('data-type');
677
+ const element = this._element;
678
+ if (/^on.+/.test(command) || type === 'selectMenu') return;
679
+
680
+ switch (command) {
681
+ case 'mirror': {
682
+ const info = this.#GetRotateValue(element);
683
+ let x = info.x;
684
+ let y = info.y;
685
+
686
+ if ((value === 'h' && !this.isVertical) || (value === 'v' && this.isVertical)) {
687
+ y = y ? '' : '180';
688
+ } else {
689
+ x = x ? '' : '180';
690
+ }
691
+
692
+ this._setRotate(element, info.r, x, y);
693
+ break;
694
+ }
695
+ case 'rotate':
696
+ this.setTransform(element, null, null, value);
697
+ break;
698
+ case 'caption':
699
+ if (!this._caption) {
700
+ const caption = Figure.CreateCaption(this._cover, this.lang.caption);
701
+ const captionText = dom.query.getEdgeChild(caption, (current) => current.nodeType === 3, false);
702
+
703
+ if (!captionText) {
704
+ caption.focus();
705
+ } else {
706
+ this.selection.setRange(captionText, 0, captionText, captionText.textContent.length);
707
+ }
708
+
709
+ this.controller.close();
710
+ } else {
711
+ dom.utils.removeItem(this._caption);
712
+ this._w.setTimeout(this.component.select.bind(this.component, element, this.kind), 0);
713
+ }
714
+
715
+ this._caption = !this._caption;
716
+ if (/\d+/.test(element.style.height) || (this.isVertical && this._caption)) {
717
+ if (/%$/.test(element.style.width) || /auto/.test(element.style.height)) {
718
+ this.deleteTransform();
719
+ } else {
720
+ this.setTransform(element, element.style.width, element.style.height, 0);
721
+ }
722
+ }
723
+ break;
724
+ case 'revert':
725
+ this._setRevert();
726
+ break;
727
+ case 'edit':
728
+ this.inst.edit(element);
729
+ break;
730
+ case 'copy': {
731
+ this.component.copy(this._container);
732
+ break;
733
+ }
734
+ case 'remove':
735
+ this.inst.destroy(element);
736
+ this.controller.close();
737
+ break;
738
+ }
739
+
740
+ if (/^__c__/.test(command)) {
741
+ this._action[command](element, value, target);
742
+ return;
743
+ }
744
+
745
+ if (/^edit$/.test(command)) return;
746
+
747
+ this.history.push(false);
748
+ if (!/^remove|caption$/.test(command)) {
749
+ this.component.select(element, this.kind);
750
+ }
751
+ }
752
+
753
+ /**
754
+ * @description Inspect the figure component format and change it to the correct format.
755
+ * @param {Node} container - The container element of the figure component.
756
+ * @param {Node} originEl - The original element of the figure component.
757
+ * @param {Node} anchorCover - The anchor cover element of the figure component.
758
+ * @param {import('./FileManager').default} [fileManagerInst=null] - FileManager module instance, if used.
759
+ */
760
+ retainFigureFormat(container, originEl, anchorCover, fileManagerInst) {
761
+ const isInline = this.component.isInline(container);
762
+ let existElement = this.format.isBlock(originEl.parentNode) || dom.check.isWysiwygFrame(originEl.parentNode) ? originEl : dom.check.isAnchor(originEl.parentNode) ? originEl.parentNode : this.format.getLine(originEl) || originEl;
763
+
764
+ if (dom.query.getParentElement(originEl, dom.check.isExcludeFormat)) {
765
+ existElement = anchorCover && anchorCover !== originEl ? anchorCover : originEl;
766
+ existElement.parentNode.replaceChild(container, existElement);
767
+ } else if (isInline && this.format.isLine(existElement)) {
768
+ const refer = isInline && /^SPAN$/i.test(originEl.parentElement.nodeName) ? originEl.parentElement : originEl;
769
+ refer.parentElement.replaceChild(container, refer);
770
+ } else if (dom.check.isListCell(existElement)) {
771
+ const refer = dom.query.getParentElement(originEl, (current) => current.parentNode === existElement);
772
+ existElement.insertBefore(container, refer);
773
+ dom.utils.removeItem(originEl);
774
+ this.nodeTransform.removeEmptyNode(refer, null, true);
775
+ } else if (this.format.isLine(existElement)) {
776
+ const refer = dom.query.getParentElement(originEl, (current) => current.parentNode === existElement);
777
+ existElement = this.nodeTransform.split(existElement, refer);
778
+ existElement.parentNode.insertBefore(container, existElement);
779
+ dom.utils.removeItem(originEl);
780
+ this.nodeTransform.removeEmptyNode(existElement, null, true);
781
+ } else {
782
+ if (this.format.isLine(existElement.parentNode)) {
783
+ const formats = existElement.parentNode;
784
+ formats.parentNode.insertBefore(container, existElement.previousSibling ? formats.nextElementSibling : formats);
785
+ if (fileManagerInst?.__updateTags.map((current) => existElement.contains(current)).length === 0) dom.utils.removeItem(existElement);
786
+ else if (dom.check.isZeroWidth(existElement)) dom.utils.removeItem(existElement);
787
+ } else {
788
+ existElement = dom.check.isFigure(existElement.parentNode) ? dom.query.getParentElement(existElement.parentNode, Figure.is) : existElement;
789
+ existElement.parentNode.replaceChild(container, existElement);
790
+ }
791
+ }
792
+ }
793
+
794
+ /**
795
+ * @description Initialize the transform style (rotation) of the element.
796
+ * @param {?Node=} node Target element, default is the current element
797
+ */
798
+ deleteTransform(node) {
799
+ if (!node) node = this._element;
800
+
801
+ const element = /** @type {HTMLElement} */ (node);
802
+ const size = (element.getAttribute('data-se-size') || '').split(',');
803
+ this.isVertical = false;
804
+
805
+ element.style.maxWidth = '';
806
+ element.style.transform = '';
807
+ element.style.transformOrigin = '';
808
+
809
+ this._deleteCaptionPosition(element);
810
+ this._applySize(size[0] || 'auto', size[1] || '', '');
811
+ }
812
+
813
+ /**
814
+ * @description Set the transform style (rotation) of the element.
815
+ * @param {Node} node Target element
816
+ * @param {?string|number} width Element's width size
817
+ * @param {?string|number} height Element's height size
818
+ */
819
+ setTransform(node, width, height, deg) {
820
+ try {
821
+ this.__preventSizechange = true;
822
+ const info = this.#GetRotateValue(node);
823
+ const slope = info.r + (deg || 0) * 1;
824
+ deg = Math.abs(slope) >= 360 ? 0 : slope;
825
+ const isVertical = (this.isVertical = /^(90|270)$/.test(Math.abs(deg).toString()));
826
+
827
+ width = numbers.get(width, 0);
828
+ height = numbers.get(height, 0);
829
+
830
+ const element = /** @type {HTMLElement} */ (node);
831
+ const dataSize = (element.getAttribute('data-se-size') || 'auto,auto').split(',');
832
+ let transOrigin = '';
833
+ if (/auto|%$/.test(dataSize[0]) && !isVertical) {
834
+ if (dataSize[0] === 'auto' && dataSize[1] === 'auto') {
835
+ this._setAutoSize();
836
+ } else {
837
+ this._setPercentSize(dataSize[0], dataSize[1]);
838
+ }
839
+ } else {
840
+ const figureInfo = Figure.GetContainer(element);
841
+ const cover = figureInfo.cover || figureInfo.inlineCover;
842
+ const offsetW = width || element.offsetWidth;
843
+ const offsetH = height || element.offsetHeight;
844
+ const w = (isVertical ? offsetH : offsetW) + 'px';
845
+ const h = (isVertical ? offsetW : offsetH) + 'px';
846
+
847
+ this._deletePercentSize();
848
+ this._applySize(offsetW + 'px', offsetH + 'px', '');
849
+
850
+ cover.style.width = w;
851
+ cover.style.height = figureInfo.caption || figureInfo.inlineCover ? '' : h;
852
+
853
+ if (isVertical) {
854
+ const transW = offsetW / 2 + 'px ' + offsetW / 2 + 'px 0';
855
+ const transH = offsetH / 2 + 'px ' + offsetH / 2 + 'px 0';
856
+ transOrigin = deg === 90 || deg === -270 ? transH : transW;
857
+ }
858
+ }
859
+
860
+ element.style.transformOrigin = transOrigin;
861
+ this._setRotate(element, deg, info.x, info.y);
862
+
863
+ if (isVertical) element.style.maxWidth = 'none';
864
+ else element.style.maxWidth = '';
865
+
866
+ this._setCaptionPosition(element);
867
+ } finally {
868
+ this.__preventSizechange = false;
869
+ }
870
+ }
871
+
872
+ /**
873
+ * @private
874
+ * @description Sets figure component properties such as cover, container, caption, and alignment.
875
+ * @param {FigureInfo} figureInfo - {target, container, cover, inlineCover, caption}
876
+ */
877
+ _setFigureInfo(figureInfo) {
878
+ this._inlineCover = figureInfo.inlineCover;
879
+ this._cover = figureInfo.cover || this._inlineCover;
880
+ this._container = figureInfo.container;
881
+ this._caption = figureInfo.caption;
882
+ this._element = figureInfo.target;
883
+ this.align = (this._container.className.match(/(?:^|\s)__se__float-(none|left|center|right)(?:$|\s)/) || [])[1] || figureInfo.target.style.float || 'none';
884
+ this.as = this._inlineCover ? 'inline' : 'block';
885
+ this.isVertical = /^(90|270)$/.test(Math.abs(this.#GetRotateValue(figureInfo.target).r).toString());
886
+ }
887
+
888
+ /**
889
+ * @private
890
+ * @description Applies rotation transformation to the target element.
891
+ * @param {HTMLElement} element Target element.
892
+ * @param {number} r Rotation degree.
893
+ * @param {number} x X-axis rotation value.
894
+ * @param {number} y Y-axis rotation value.
895
+ */
896
+ _setRotate(element, r, x, y) {
897
+ let width = (element.offsetWidth - element.offsetHeight) * (/^-/.test(r + '') ? 1 : -1);
898
+ let translate = '';
899
+
900
+ if (/[1-9]/.test(r + '') && (x || y)) {
901
+ translate = x ? 'Y' : 'X';
902
+
903
+ switch (r + '') {
904
+ case '90':
905
+ translate = x && y ? 'X' : y ? translate : '';
906
+ break;
907
+ case '270':
908
+ width *= -1;
909
+ translate = x && y ? 'Y' : x ? translate : '';
910
+ break;
911
+ case '-90':
912
+ translate = x && y ? 'Y' : x ? translate : '';
913
+ break;
914
+ case '-270':
915
+ width *= -1;
916
+ translate = x && y ? 'X' : y ? translate : '';
917
+ break;
918
+ default:
919
+ translate = '';
920
+ }
921
+ }
922
+
923
+ if (r % 180 === 0) {
924
+ element.style.maxWidth = '';
925
+ }
926
+
927
+ element.style.transform = 'rotate(' + r + 'deg)' + (x ? ' rotateX(' + x + 'deg)' : '') + (y ? ' rotateY(' + y + 'deg)' : '') + (translate ? ' translate' + translate + '(' + width + 'px)' : '');
928
+ }
929
+
930
+ /**
931
+ * @private
932
+ * @description Applies size adjustments to the figure element.
933
+ * @param {string|number} w Width value.
934
+ * @param {string|number} h Height value.
935
+ * @param {string} direction Resize direction.
936
+ */
937
+ _applySize(w, h, direction) {
938
+ const onlyW = /^(rw|lw)$/.test(direction) && /\d+/.test(this._element.style.height);
939
+ const onlyH = /^(th|bh)$/.test(direction) && /\d+/.test(this._element.style.width);
940
+ h = /** @type {string} */ (h || (this.autoRatio ? this.autoRatio.current || this.autoRatio.default : h));
941
+ w = /** @type {string} */ (numbers.is(w) ? w + this.sizeUnit : w);
942
+
943
+ if (!/%$/.test(w) && !/%$/.test(h) && !onlyW && !onlyH) this._deletePercentSize();
944
+
945
+ const sizeTarget = this._cover || this._element;
946
+
947
+ if (this.autoRatio) this._cover.style.width = w;
948
+ if (!onlyH) {
949
+ sizeTarget.style.width = this._element.style.width = w;
950
+ }
951
+ if (!onlyW) {
952
+ h = numbers.is(h) ? h + this.sizeUnit : h;
953
+ sizeTarget.style.height = this._element.style.height = this.autoRatio && !this.isVertical ? '100%' : h;
954
+ if (this.autoRatio) {
955
+ this._cover.style.height = h;
956
+ this.__setCoverPaddingBottom(w, h);
957
+ }
958
+ }
959
+
960
+ if (this.align === 'center') this.setAlign(this._element, this.align);
961
+
962
+ // save current size
963
+ this._saveCurrentSize();
964
+ }
965
+
966
+ /**
967
+ * @private
968
+ * @description Sets padding-bottom for cover elements based on width and height.
969
+ * @param {string} w Width value.
970
+ * @param {string} h Height value.
971
+ */
972
+ __setCoverPaddingBottom(w, h) {
973
+ if (this._inlineCover === this._cover) return;
974
+
975
+ this._cover.style.height = h;
976
+ if (/%$/.test(w) && this.align === 'center') {
977
+ this._cover.style.paddingBottom = !/%$/.test(h) ? h : numbers.get((numbers.get(h, 2) / 100) * numbers.get(w, 2), 2) + '%';
978
+ } else {
979
+ this._cover.style.paddingBottom = h;
980
+ }
981
+ }
982
+
983
+ /**
984
+ * @private
985
+ * @description Sets the figure element to its auto size.
986
+ */
987
+ _setAutoSize() {
988
+ if (this._caption) this._caption.style.marginTop = '';
989
+ this.deleteTransform();
990
+ this._deletePercentSize();
991
+
992
+ if (this.autoRatio) {
993
+ this._setPercentSize('100%', this.autoRatio.current || this.autoRatio.default);
994
+ } else {
995
+ this._element.style.maxWidth = '';
996
+ this._element.style.width = '';
997
+ this._element.style.height = '';
998
+ this._cover.style.width = '';
999
+ this._cover.style.height = '';
1000
+ }
1001
+
1002
+ this.setAlign(this._element, this.align);
1003
+
1004
+ // save current size
1005
+ this._saveCurrentSize();
1006
+ }
1007
+
1008
+ /**
1009
+ * @private
1010
+ * @description Sets the figure element's size in percentage.
1011
+ * @param {string|number} w Width percentage.
1012
+ * @param {string|number} h Height percentage.
1013
+ */
1014
+ _setPercentSize(w, h) {
1015
+ if (!h) h = this.autoRatio ? (/%$/.test(this.autoRatio.current) ? this.autoRatio.current : this.autoRatio.default) : h;
1016
+ h = h && !/%$/.test(h + '') && !numbers.get(h, 0) ? (numbers.is(h) ? h + '%' : h) : numbers.is(h) ? h + this.sizeUnit : h || (this.autoRatio ? this.autoRatio.default : '');
1017
+
1018
+ const heightPercentage = /%$/.test(h + '');
1019
+ this._container.style.width = String(numbers.is(w) ? w + '%' : w);
1020
+ this._container.style.height = '';
1021
+
1022
+ // exceptionFormat
1023
+ if (this._element === this._cover && !this.options.get('strictMode').formatFilter) {
1024
+ this._saveCurrentSize();
1025
+ return;
1026
+ }
1027
+
1028
+ if (this._inlineCover !== this._cover) {
1029
+ this._cover.style.width = '100%';
1030
+ this._cover.style.height = String(h);
1031
+ }
1032
+ this._element.style.width = '100%';
1033
+ this._element.style.maxWidth = '';
1034
+ this._element.style.height = String(this.autoRatio ? '100%' : heightPercentage ? '' : h);
1035
+
1036
+ if (this.align === 'center') this.setAlign(this._element, this.align);
1037
+ if (this.autoRatio) {
1038
+ this.__setCoverPaddingBottom(String(w), String(h));
1039
+ }
1040
+
1041
+ this._setCaptionPosition(this._element);
1042
+
1043
+ // save current size
1044
+ this._saveCurrentSize();
1045
+ }
1046
+
1047
+ /**
1048
+ * @private
1049
+ * @description Deletes percentage-based sizing from the figure element.
1050
+ */
1051
+ _deletePercentSize() {
1052
+ this._cover.style.width = '';
1053
+ this._cover.style.height = '';
1054
+ this._container.style.width = '';
1055
+ this._container.style.height = '';
1056
+
1057
+ dom.utils.removeClass(this._container, this._floatClassStr);
1058
+ dom.utils.addClass(this._container, '__se__float-' + this.align);
1059
+
1060
+ if (this.align === 'center') this.setAlign(this._element, this.align);
1061
+ }
1062
+
1063
+ /**
1064
+ * @private
1065
+ * @description Reverts the figure element to its previously saved size.
1066
+ */
1067
+ _setRevert() {
1068
+ this.setSize(this.__revertSize.w, this.__revertSize.h);
1069
+ }
1070
+
1071
+ /**
1072
+ * @private
1073
+ * @description Updates the figure's alignment icon.
1074
+ */
1075
+ _setAlignIcon() {
1076
+ if (!this.alignButton) return;
1077
+ dom.utils.changeElement(this.alignButton.firstElementChild, this._alignIcons[this.align]);
1078
+ }
1079
+
1080
+ /**
1081
+ * @private
1082
+ * @description Updates the figure's block/inline format icon.
1083
+ */
1084
+ _setAsIcon() {
1085
+ if (!this.asButton) return;
1086
+ dom.utils.changeElement(this.asButton.firstElementChild, this.icons[`as_${this.as}`]);
1087
+ }
1088
+
1089
+ /**
1090
+ * @private
1091
+ * @description Saves the current size of the figure component.
1092
+ */
1093
+ _saveCurrentSize() {
1094
+ if (this.__preventSizechange) return;
1095
+
1096
+ const dataSize = (this._element.getAttribute('data-se-size') || ',').split(',');
1097
+ this.__revertSize.w = dataSize[0];
1098
+ this.__revertSize.h = dataSize[1];
1099
+
1100
+ const size = this.getSize(this._element);
1101
+ // add too width, height attribute
1102
+ this._element.setAttribute('width', size.w.replace('px', ''));
1103
+ this._element.setAttribute('height', size.h.replace('px', ''));
1104
+ this._element.setAttribute('data-se-size', size.w + ',' + size.h);
1105
+ if (this.autoRatio) {
1106
+ this.autoRatio.current = /%$/.test(size.h) ? size.h : '';
1107
+ }
1108
+ }
1109
+
1110
+ /**
1111
+ * @private
1112
+ * @description Adjusts the position of the caption within the figure.
1113
+ * @param {HTMLElement} element Target element.
1114
+ */
1115
+ _setCaptionPosition(element) {
1116
+ const figcaption = /** @type {HTMLElement} */ (dom.query.getEdgeChild(dom.query.getParentElement(element, 'FIGURE'), 'FIGCAPTION', false));
1117
+ if (figcaption) {
1118
+ figcaption.style.marginTop = (this.isVertical ? element.offsetWidth - element.offsetHeight : 0) + 'px';
1119
+ }
1120
+ }
1121
+
1122
+ /**
1123
+ * @private
1124
+ * @description Removes the margin top property from the figure caption.
1125
+ * @param {HTMLElement} element Target element.
1126
+ */
1127
+ _deleteCaptionPosition(element) {
1128
+ const figcaption = /** @type {HTMLElement} */ (dom.query.getEdgeChild(dom.query.getParentElement(element, 'FIGURE'), 'FIGCAPTION', false));
1129
+ if (figcaption) {
1130
+ figcaption.style.marginTop = '';
1131
+ }
1132
+ }
1133
+
1134
+ /**
1135
+ * @private
1136
+ * @description Displays or hides the resize handles of the figure component.
1137
+ * @param {boolean} display Whether to display resize handles.
1138
+ */
1139
+ _displayResizeHandles(display) {
1140
+ const type = !display ? 'none' : 'flex';
1141
+ this.controller.form.style.display = type;
1142
+
1143
+ const _figure = this.editor.frameContext.get('_figure');
1144
+ const resizeHandles = _figure.handles;
1145
+ for (let i = 0, len = resizeHandles.length; i < len; i++) {
1146
+ resizeHandles[i].style.display = type;
1147
+ }
1148
+
1149
+ if (type === 'none') {
1150
+ dom.utils.addClass(_figure.main, 'se-resize-ing');
1151
+ this.__onResizeESCEvent = this.eventManager.addGlobalEvent('keydown', this.__containerResizingESC);
1152
+ } else {
1153
+ dom.utils.removeClass(_figure.main, 'se-resize-ing');
1154
+ }
1155
+ }
1156
+
1157
+ /**
1158
+ * @private
1159
+ * @description Removes the resize event listeners.
1160
+ */
1161
+ _offResizeEvent() {
1162
+ this.component._removeDragEvent();
1163
+ this.eventManager.removeGlobalEvent(this.__onContainerEvent);
1164
+ this.eventManager.removeGlobalEvent(this.__offContainerEvent);
1165
+ this.eventManager.removeGlobalEvent(this.__onResizeESCEvent);
1166
+
1167
+ this._displayResizeHandles(true);
1168
+ this.ui._offCurrentController();
1169
+ this.ui.disableBackWrapper();
1170
+ }
1171
+
1172
+ /**
1173
+ * @private
1174
+ * @description Sets up drag event handling for the figure component.
1175
+ * @param {Node} figureMain The main figure container element.
1176
+ */
1177
+ _setDragEvent(figureMain) {
1178
+ const dragHandle = this.editor.frameContext.get('wrapper').querySelector('.se-drag-handle');
1179
+ dom.utils.removeClass(dragHandle, 'se-drag-handle-full');
1180
+
1181
+ dragHandle.style.opacity = '';
1182
+ dragHandle.style.width = '';
1183
+ dragHandle.style.height = '';
1184
+
1185
+ _DragHandle.set('__dragHandler', dragHandle);
1186
+ _DragHandle.set('__dragContainer', this._container);
1187
+ _DragHandle.set('__dragCover', this._cover);
1188
+ _DragHandle.set('__dragMove', this.#OnScrollDragHandler.bind(this, dragHandle, figureMain));
1189
+
1190
+ _DragHandle.get('__dragMove')();
1191
+
1192
+ dragHandle.style.display = 'block';
1193
+ }
1194
+
1195
+ /**
1196
+ * @param {HTMLElement} dragHandle Drag handle element
1197
+ * @param {HTMLElement} figureMain Figure container element
1198
+ */
1199
+ #OnScrollDragHandler(dragHandle, figureMain) {
1200
+ dragHandle.style.display = 'block';
1201
+ dragHandle.style.left = figureMain.offsetLeft + (this.options.get('_rtl') ? dragHandle.offsetWidth : figureMain.offsetWidth - dragHandle.offsetWidth * 1.5) + 'px';
1202
+ dragHandle.style.top = figureMain.offsetTop - dragHandle.offsetHeight + 'px';
1203
+ }
1204
+
1205
+ /**
1206
+ * @param {MouseEvent} e Event object
1207
+ */
1208
+ #OnResizeContainer(e) {
1209
+ e.stopPropagation();
1210
+ e.preventDefault();
1211
+
1212
+ const inst = _DragHandle.get('__figureInst');
1213
+ if (!inst) return;
1214
+
1215
+ const eventTarget = dom.query.getEventTarget(e);
1216
+ const direction = (inst._resize_direction = eventTarget.classList[0]);
1217
+ inst._resizeClientX = e.clientX;
1218
+ inst._resizeClientY = e.clientY;
1219
+ inst.editor.frameContext.get('_figure').main.style.float = /l/.test(direction) ? 'right' : /r/.test(direction) ? 'left' : 'none';
1220
+ this.ui.enableBackWrapper(DIRECTION_CURSOR_MAP[direction]);
1221
+
1222
+ const { w, h } = this.getSize(inst._element);
1223
+ __resizing_p_wh = __resizing_p_ow = false;
1224
+ __resizing_cw = __resizing_sw = 0;
1225
+ if (!this.isVertical) {
1226
+ const pw = !w || /auto|%$/.test(w);
1227
+ const ph = !h || /auto|%$/.test(h);
1228
+ if (DIR_DIAGONAL.includes(direction) && pw && ph) {
1229
+ __resizing_p_wh = true;
1230
+ } else if (DIR_W.includes(direction) && pw) {
1231
+ __resizing_p_ow = true;
1232
+ }
1233
+
1234
+ if (__resizing_p_wh || __resizing_p_ow) {
1235
+ const sizeTarget = inst._cover || inst._element;
1236
+ __resizing_sw = sizeTarget.offsetWidth;
1237
+ __resizing_cw = converter.getWidthInPercentage(sizeTarget, this.editor.frameContext.get('wysiwygFrame')) / 100;
1238
+ }
1239
+ }
1240
+
1241
+ inst.__onContainerEvent = inst.eventManager.addGlobalEvent('mousemove', inst.__containerResizing);
1242
+ inst.__offContainerEvent = inst.eventManager.addGlobalEvent('mouseup', inst.__containerResizingOff);
1243
+ inst._displayResizeHandles(false);
1244
+
1245
+ const _display = this.editor.frameContext.get('_figure').display;
1246
+ _display.style.display = 'block';
1247
+ dom.utils.changeTxt(_display, w + ' x ' + h);
1248
+ }
1249
+
1250
+ /**
1251
+ * @description Handles the resizing of the figure container.
1252
+ * @param {MouseEvent} e Mouse event.
1253
+ */
1254
+ #ContainerResizing(e) {
1255
+ const direction = this._resize_direction;
1256
+ const clientX = e.clientX;
1257
+ const clientY = e.clientY;
1258
+
1259
+ let resultW = this._element_w;
1260
+ let resultH = this._element_h;
1261
+
1262
+ const w = resultW + (/r/.test(direction) ? clientX - this._resizeClientX : this._resizeClientX - clientX);
1263
+ const h = resultH + (/b/.test(direction) ? clientY - this._resizeClientY : this._resizeClientY - clientY);
1264
+ const wh = (resultH / resultW) * w;
1265
+ const resizeBorder = this.editor.frameContext.get('_figure').border;
1266
+
1267
+ if (/t/.test(direction)) resizeBorder.style.top = resultH - (/h/.test(direction) ? h : wh) + 'px';
1268
+ if (/l/.test(direction)) resizeBorder.style.left = resultW - w + 'px';
1269
+
1270
+ if (/r|l/.test(direction)) {
1271
+ resizeBorder.style.width = w + 'px';
1272
+ resultW = w;
1273
+ }
1274
+
1275
+ if (/^(t|b)[^h]$/.test(direction)) {
1276
+ resizeBorder.style.height = wh + 'px';
1277
+ resultH = wh;
1278
+ } else if (/^(t|b)h$/.test(direction)) {
1279
+ resizeBorder.style.height = h + 'px';
1280
+ resultH = h;
1281
+ }
1282
+
1283
+ this._resize_w = /** @type {number} */ (/h$/.test(direction) ? this._width : Math.round(resultW));
1284
+ this._resize_h = /** @type {number} */ (/w$/.test(direction) ? this._height : Math.round(resultH));
1285
+ const rw = __resizing_cw ? (this._resize_w / __resizing_sw) * __resizing_cw * 100 : this._resize_w;
1286
+ dom.utils.changeTxt(this.editor.frameContext.get('_figure').display, __resizing_cw ? numbers.get(rw > 100 ? 100 : rw, 2).toFixed(2) + '%' : rw + ' x ' + this._resize_h);
1287
+ }
1288
+
1289
+ /**
1290
+ * @description Finalizes the resizing process of the figure container.
1291
+ */
1292
+ #ContainerResizingOff() {
1293
+ this._offResizeEvent();
1294
+
1295
+ // set size
1296
+ let w = this.isVertical ? this._resize_h : this._resize_w;
1297
+ let h = this.isVertical ? this._resize_w : this._resize_h;
1298
+ w = Math.round(w) || w;
1299
+ h = Math.round(h) || h;
1300
+
1301
+ if (!this.isVertical && !/%$/.test(w + '')) {
1302
+ const limit =
1303
+ this.editor.frameContext.get('wysiwygFrame').clientWidth -
1304
+ numbers.get(this.editor.frameContext.get('wwComputedStyle').getPropertyValue('padding-left')) +
1305
+ numbers.get(this.editor.frameContext.get('wwComputedStyle').getPropertyValue('padding-right')) -
1306
+ 2;
1307
+ if (numbers.get(w, 0) > limit) {
1308
+ h = Math.round((h / w) * limit);
1309
+ w = limit;
1310
+ }
1311
+ }
1312
+
1313
+ if (__resizing_p_wh || __resizing_p_ow) {
1314
+ const sizeTarget = this._cover || this._element;
1315
+ w = (w / sizeTarget.offsetWidth) * __resizing_cw * 100;
1316
+ const wp = numbers.get(w > 100 ? 100 : w, 2) + '%';
1317
+ this._setPercentSize(wp, __resizing_p_ow ? this.getSize(this._element).h : '');
1318
+ } else {
1319
+ this._applySize(w, h, this._resize_direction);
1320
+ if (this.isVertical) this.setTransform(this._element, w, h, 0);
1321
+ }
1322
+
1323
+ this.history.push(false);
1324
+ this.component.select(this._element, this.kind);
1325
+ }
1326
+
1327
+ /**
1328
+ * @description Cancels the resizing process when the escape key is pressed.
1329
+ * @param {KeyboardEvent} e Keyboard event.
1330
+ */
1331
+ #ContainerResizingESC(e) {
1332
+ if (!keyCodeMap.isEsc(e.code)) return;
1333
+ this._offResizeEvent();
1334
+ this.component.select(this._element, this.kind);
1335
+ }
1336
+
1337
+ /**
1338
+ * @param { "none"|"left"|"center"|"right"} value align value
1339
+ */
1340
+ #SetMenuAlign(value) {
1341
+ this.setAlign(this._element, value);
1342
+ this.selectMenu_align.close();
1343
+ this.component.select(this._element, this.kind);
1344
+ }
1345
+
1346
+ /**
1347
+ * @param {"block"|"inline"} value Format style
1348
+ */
1349
+ #SetMenuAs(value) {
1350
+ this.convertAsFormat(this._element, value);
1351
+ this.selectMenu_as.close();
1352
+ }
1353
+
1354
+ /**
1355
+ * @param {"auto"|number} value Resize value
1356
+ */
1357
+ #SetResize(value) {
1358
+ if (value === 'auto') {
1359
+ this.deleteTransform();
1360
+ this._setAutoSize();
1361
+ } else {
1362
+ let dataY = this.getSize(this._element).h;
1363
+ if (this.isVertical) {
1364
+ const dataSize = (this._element.getAttribute('data-se-size') || ',').split(',');
1365
+ if (dataSize[1]) dataY = dataSize[1];
1366
+ }
1367
+
1368
+ this.deleteTransform();
1369
+ this._setPercentSize(value * 1, numbers.get(dataY, 0) === null || !/%$/.test(dataY) ? '' : dataY);
1370
+ }
1371
+
1372
+ this.selectMenu_resize.close();
1373
+ this.component.select(this._element, this.kind);
1374
+ }
1375
+
1376
+ #OffFigureContainer() {
1377
+ this.editor.frameContext.get('_figure').main.style.display = 'none';
1378
+ this.editor._figureContainer = null;
1379
+ }
1380
+
1381
+ #OnClick_alignButton() {
1382
+ this.selectMenu_align.open('', '[data-command="' + this.align + '"]');
1383
+ }
1384
+
1385
+ #OnClick_asButton() {
1386
+ this.selectMenu_as.open('', '[data-command="' + this.as + '"]');
1387
+ }
1388
+
1389
+ #OnClick_resizeButton() {
1390
+ const size = this.getSize(this._element);
1391
+ const w = size.w;
1392
+ const h = size.h;
1393
+ let command = '';
1394
+ if (this.autoRatio) {
1395
+ if (h === this.autoRatio.default && /%$/.test(w)) {
1396
+ const nw = numbers.get(w);
1397
+ if (nw === 100) command = 'auto';
1398
+ else command = 'resize_percent' + nw;
1399
+ }
1400
+ } else if (h === 'auto') {
1401
+ if (w === 'auto') {
1402
+ command = 'auto';
1403
+ } else if (/%$/.test(w)) {
1404
+ command = 'resize_percent' + numbers.get(w);
1405
+ }
1406
+ }
1407
+
1408
+ this.selectMenu_resize.open('', '[data-command="' + command + '"]');
1409
+ }
1410
+
1411
+ /**
1412
+ * @param {Node} element Target element
1413
+ * @returns {{ r: *, x: *, y: * }} Rotation value
1414
+ */
1415
+ #GetRotateValue(element) {
1416
+ const transform = /** @type {HTMLElement} */ (element).style.transform;
1417
+ if (!transform) return { r: 0, x: '', y: '' };
1418
+ return {
1419
+ r: Number((transform.match(/rotate\(([-0-9]+)deg\)/) || [])[1] || 0),
1420
+ x: (transform.match(/rotateX\(([-0-9]+)deg\)/) || [])[1] || '',
1421
+ y: (transform.match(/rotateY\(([-0-9]+)deg\)/) || [])[1] || ''
1422
+ };
1423
+ }
1424
+ }
1425
+
1426
+ function CreateAlign(inst, button) {
1427
+ if (!button) return null;
1428
+
1429
+ const icons = [inst._alignIcons.none, inst._alignIcons.left, inst._alignIcons.center, inst._alignIcons.right];
1430
+ const langs = [inst.lang.basic, inst.lang.left, inst.lang.center, inst.lang.right];
1431
+ const commands = ['none', 'left', 'center', 'right'];
1432
+ const html = [];
1433
+ const items = [];
1434
+ for (let i = 0; i < commands.length; i++) {
1435
+ html.push(/*html*/ `
1436
+ <button type="button" class="se-btn-list se-tooltip" data-command="${commands[i]}" data-type="selectMenu" >
1437
+ ${icons[i]}
1438
+ <span class="se-tooltip-inner">
1439
+ <span class="se-tooltip-text">${langs[i]}</span>
1440
+ </span>
1441
+ </button>`);
1442
+ items.push(commands[i]);
1443
+ }
1444
+
1445
+ return { html: html, items: items };
1446
+ }
1447
+
1448
+ function CreateAs(inst, button) {
1449
+ if (!button) return null;
1450
+
1451
+ const icons = [inst.icons.as_block, inst.icons.as_inline];
1452
+ const langs = [inst.lang.asBlock, inst.lang.asInline];
1453
+ const commands = ['block', 'inline'];
1454
+ const html = [];
1455
+ const items = [];
1456
+ for (let i = 0; i < commands.length; i++) {
1457
+ html.push(/*html*/ `
1458
+ <button type="button" class="se-btn-list se-tooltip" data-command="${commands[i]}" data-type="selectMenu" >
1459
+ ${icons[i]}
1460
+ <span class="se-tooltip-inner">
1461
+ <span class="se-tooltip-text">${langs[i]}</span>
1462
+ </span>
1463
+ </button>`);
1464
+ items.push(commands[i]);
1465
+ }
1466
+
1467
+ return { html: html, items: items };
1468
+ }
1469
+
1470
+ function CreateResize(editor, button) {
1471
+ if (!button) return null;
1472
+
1473
+ const items = button.getAttribute('data-value').split(',');
1474
+ const html = [];
1475
+ for (let i = 0, n, c, v, l; i < items.length; i++) {
1476
+ v = items[i];
1477
+ n = numbers.is(v);
1478
+ c = n ? 'resize_percent' + v : 'auto';
1479
+ l = n ? v + '%' : editor.lang.autoSize;
1480
+ html.push('<button type="button" class="se-btn-list" data-command="' + c + '" data-value="' + v + '"><span>' + l + '</span></button>');
1481
+ }
1482
+
1483
+ return { html: html, items: items };
1484
+ }
1485
+
1486
+ function CreateHTML_resizeDot() {
1487
+ const html = /*html*/ `
1488
+ <div class="se-resize-dot">
1489
+ <span class="tl"></span>
1490
+ <span class="tr"></span>
1491
+ <span class="bl"></span>
1492
+ <span class="br"></span>
1493
+ <span class="lw"></span>
1494
+ <span class="th"></span>
1495
+ <span class="rw"></span>
1496
+ <span class="bh"></span>
1497
+ <div class="se-resize-display"></div>
1498
+ </div>`;
1499
+
1500
+ return dom.utils.createElement('DIV', { class: 'se-controller se-resizing-container', style: 'display: none;' }, html);
1501
+ }
1502
+
1503
+ function GET_CONTROLLER_BUTTONS(group) {
1504
+ const g = group.split('_');
1505
+ const command = g[0];
1506
+ const value = g[1];
1507
+ let c, v, l, t, i;
1508
+
1509
+ switch (command) {
1510
+ case 'resize':
1511
+ c = 'onresize';
1512
+ v = value;
1513
+ l = 'resize';
1514
+ i = 'resize';
1515
+ break;
1516
+ case 'auto':
1517
+ c = 'auto';
1518
+ l = 'autoSize';
1519
+ i = 'auto_size';
1520
+ break;
1521
+ case 'rotate':
1522
+ c = 'rotate';
1523
+ v = value === 'l' ? -90 : value === 'r' ? 90 : numbers.get(value);
1524
+ l = v < 0 ? 'rotateLeft' : 'rotateRight';
1525
+ i = v < 0 ? 'rotate_left' : 'rotate_right';
1526
+ break;
1527
+ case 'mirror':
1528
+ c = 'mirror';
1529
+ v = value;
1530
+ l = value === 'h' ? 'mirrorHorizontal' : 'mirrorVertical';
1531
+ i = value === 'h' ? 'mirror_horizontal' : 'mirror_vertical';
1532
+ break;
1533
+ case 'align':
1534
+ c = 'onalign';
1535
+ l = 'align';
1536
+ i = 'align_justify';
1537
+ break;
1538
+ case 'caption':
1539
+ c = 'caption';
1540
+ l = 'caption';
1541
+ i = 'caption';
1542
+ break;
1543
+ case 'revert':
1544
+ c = 'revert';
1545
+ l = 'revert';
1546
+ i = 'revert';
1547
+ break;
1548
+ case 'edit':
1549
+ c = 'edit';
1550
+ l = 'edit';
1551
+ i = 'edit';
1552
+ break;
1553
+ case 'as':
1554
+ c = 'onas';
1555
+ l = 'blockStyle';
1556
+ i = 'as_block';
1557
+ break;
1558
+ case 'copy':
1559
+ c = 'copy';
1560
+ l = 'copy';
1561
+ i = 'copy';
1562
+ break;
1563
+ case 'remove':
1564
+ c = 'remove';
1565
+ l = 'remove';
1566
+ i = 'delete';
1567
+ break;
1568
+ }
1569
+
1570
+ if (!c) return null;
1571
+
1572
+ return {
1573
+ c: c,
1574
+ v: v,
1575
+ l: l,
1576
+ t: t,
1577
+ i: i
1578
+ };
1579
+ }
1580
+
1581
+ function CreateHTML_controller(inst, controls) {
1582
+ let html = null;
1583
+
1584
+ if (controls?.length > 0) {
1585
+ const { lang, icons } = inst;
1586
+ html = '<div class="se-arrow se-arrow-up"></div>';
1587
+ for (let i = 0, group; i < controls.length; i++) {
1588
+ group = controls[i];
1589
+ html += '<div class="se-btn-group">';
1590
+ for (let j = 0, len = group.length, m; j < len; j++) {
1591
+ m = group[j];
1592
+
1593
+ if (typeof m?.action === 'function') {
1594
+ const g = m;
1595
+ m = {
1596
+ c: `__c__${g.command}`,
1597
+ v: g.value || '',
1598
+ l: g.title,
1599
+ i: g.icon
1600
+ };
1601
+ inst._action[m.c] = g.action;
1602
+ } else {
1603
+ m = GET_CONTROLLER_BUTTONS(m);
1604
+ if (!m) continue;
1605
+ }
1606
+
1607
+ html += /*html*/ `
1608
+ <button type="button" data-command="${m.c}" data-value="${m.v}" class="se-btn se-tooltip">
1609
+ ${icons[m.i] || m.i}
1610
+ <span class="se-tooltip-inner"><span class="se-tooltip-text">${lang[m.l] || m.l}</span></span>
1611
+ </button>`;
1612
+ }
1613
+ html += '</div>';
1614
+ }
1615
+ }
1616
+
1617
+ return dom.utils.createElement('DIV', { class: 'se-controller se-controller-resizing' + (!html ? ' se-empty-controller' : '') }, html);
1618
+ }
1619
+
1620
+ export default Figure;