suneditor 3.0.0-beta.21 → 3.0.0-beta.23

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 (95) hide show
  1. package/CONTRIBUTING.md +4 -6
  2. package/README.md +29 -12
  3. package/dist/suneditor.min.js +1 -1
  4. package/package.json +11 -11
  5. package/src/core/base/eventHandlers/handler_ww_key_input.js +1 -1
  6. package/src/core/base/eventManager.js +4 -4
  7. package/src/core/class/component.js +112 -32
  8. package/src/core/class/format.js +37 -48
  9. package/src/core/class/html.js +8 -8
  10. package/src/core/class/menu.js +2 -2
  11. package/src/core/class/nodeTransform.js +7 -10
  12. package/src/core/class/offset.js +3 -3
  13. package/src/core/class/selection.js +3 -1
  14. package/src/core/class/ui.js +3 -3
  15. package/src/core/config/options.js +8 -2
  16. package/src/core/editor.js +2 -2
  17. package/src/core/section/actives.js +3 -3
  18. package/src/core/section/constructor.js +4 -4
  19. package/src/helper/clipboard.js +22 -7
  20. package/src/helper/dom/domQuery.js +8 -16
  21. package/src/helper/dom/domUtils.js +3 -10
  22. package/src/modules/ApiManager.js +10 -10
  23. package/src/modules/Browser.js +3 -6
  24. package/src/modules/ColorPicker.js +1 -1
  25. package/src/modules/Controller.js +2 -2
  26. package/src/modules/Figure.js +46 -28
  27. package/src/modules/FileManager.js +4 -6
  28. package/src/modules/HueSlider.js +7 -9
  29. package/src/modules/Modal.js +4 -4
  30. package/src/modules/SelectMenu.js +5 -6
  31. package/src/plugins/browser/audioGallery.js +9 -5
  32. package/src/plugins/browser/fileBrowser.js +10 -6
  33. package/src/plugins/browser/fileGallery.js +9 -5
  34. package/src/plugins/browser/imageGallery.js +8 -4
  35. package/src/plugins/browser/videoGallery.js +9 -5
  36. package/src/plugins/command/exportPDF.js +7 -3
  37. package/src/plugins/command/fileUpload.js +23 -13
  38. package/src/plugins/dropdown/align.js +6 -2
  39. package/src/plugins/dropdown/backgroundColor.js +8 -4
  40. package/src/plugins/dropdown/font.js +6 -4
  41. package/src/plugins/dropdown/fontColor.js +8 -4
  42. package/src/plugins/dropdown/formatBlock.js +6 -2
  43. package/src/plugins/dropdown/hr.js +7 -3
  44. package/src/plugins/dropdown/layout.js +6 -2
  45. package/src/plugins/dropdown/lineHeight.js +6 -2
  46. package/src/plugins/dropdown/paragraphStyle.js +15 -11
  47. package/src/plugins/dropdown/table.js +24 -20
  48. package/src/plugins/dropdown/template.js +6 -2
  49. package/src/plugins/dropdown/textStyle.js +6 -2
  50. package/src/plugins/field/mention.js +14 -10
  51. package/src/plugins/input/fontSize.js +12 -8
  52. package/src/plugins/modal/audio.js +10 -4
  53. package/src/plugins/modal/drawing.js +2 -2
  54. package/src/plugins/modal/embed.js +23 -32
  55. package/src/plugins/modal/image.js +36 -36
  56. package/src/plugins/modal/math.js +2 -2
  57. package/src/plugins/modal/video.js +25 -39
  58. package/src/plugins/popup/anchor.js +6 -5
  59. package/src/typedef.js +11 -0
  60. package/types/core/class/component.d.ts +28 -4
  61. package/types/core/class/format.d.ts +6 -6
  62. package/types/core/class/menu.d.ts +2 -2
  63. package/types/core/class/ui.d.ts +1 -1
  64. package/types/core/config/options.d.ts +15 -3
  65. package/types/modules/Browser.d.ts +1 -1
  66. package/types/modules/Controller.d.ts +2 -2
  67. package/types/modules/Figure.d.ts +3 -4
  68. package/types/modules/Modal.d.ts +3 -3
  69. package/types/modules/SelectMenu.d.ts +2 -2
  70. package/types/plugins/browser/audioGallery.d.ts +29 -18
  71. package/types/plugins/browser/fileBrowser.d.ts +38 -27
  72. package/types/plugins/browser/fileGallery.d.ts +29 -18
  73. package/types/plugins/browser/imageGallery.d.ts +24 -16
  74. package/types/plugins/browser/videoGallery.d.ts +29 -18
  75. package/types/plugins/command/exportPDF.d.ts +17 -10
  76. package/types/plugins/command/fileUpload.d.ts +64 -27
  77. package/types/plugins/dropdown/align.d.ts +12 -8
  78. package/types/plugins/dropdown/backgroundColor.d.ts +28 -18
  79. package/types/plugins/dropdown/font.d.ts +12 -12
  80. package/types/plugins/dropdown/fontColor.d.ts +28 -18
  81. package/types/plugins/dropdown/formatBlock.d.ts +12 -8
  82. package/types/plugins/dropdown/hr.d.ts +15 -11
  83. package/types/plugins/dropdown/layout.d.ts +15 -11
  84. package/types/plugins/dropdown/lineHeight.d.ts +15 -11
  85. package/types/plugins/dropdown/paragraphStyle.d.ts +31 -27
  86. package/types/plugins/dropdown/table.d.ts +28 -15
  87. package/types/plugins/dropdown/template.d.ts +15 -11
  88. package/types/plugins/dropdown/textStyle.d.ts +19 -11
  89. package/types/plugins/field/mention.d.ts +58 -34
  90. package/types/plugins/input/fontSize.d.ts +44 -26
  91. package/types/plugins/modal/audio.d.ts +14 -0
  92. package/types/plugins/modal/embed.d.ts +15 -0
  93. package/types/plugins/modal/image.d.ts +20 -0
  94. package/types/plugins/modal/video.d.ts +15 -0
  95. package/types/typedef.d.ts +1 -0
@@ -159,7 +159,7 @@ Offset.prototype = {
159
159
  const topArea = this.frameContext.get('topArea');
160
160
  const wFrame = this.frameContext.get('wysiwygFrame');
161
161
 
162
- node = node || topArea;
162
+ node ||= topArea;
163
163
 
164
164
  if (!isElement(node)) {
165
165
  return { top: 0, left: 0, fixedTop: 0, fixedLeft: 0, width: 0, height: 0 };
@@ -206,7 +206,7 @@ Offset.prototype = {
206
206
  const topArea = this.frameContext.get('topArea');
207
207
  let isTop = false;
208
208
  let targetAbs = false;
209
- if (!node) node = topArea;
209
+ node ||= topArea;
210
210
  if (node === topArea) isTop = true;
211
211
  if (!isTop && isElement(node)) {
212
212
  targetAbs = _w.getComputedStyle(node).position === 'absolute';
@@ -607,7 +607,7 @@ Offset.prototype = {
607
607
  element.style.display = 'block';
608
608
 
609
609
  let positionTop = position === 'top';
610
- range = range || this.selection.getRange();
610
+ range ||= this.selection.getRange();
611
611
  const rectsObj = this.selection.getRects(range, positionTop ? 'start' : 'end');
612
612
  positionTop = rectsObj.position === 'start';
613
613
 
@@ -150,6 +150,8 @@ Selection_.prototype = {
150
150
  const range = this.frameContext.get('_wd').createRange();
151
151
 
152
152
  try {
153
+ so = Math.min(so, sc.textContent?.length || 0);
154
+ eo = Math.min(eo, ec.textContent?.length || 0);
153
155
  range.setStart(sc, so);
154
156
  range.setEnd(ec, eo);
155
157
  this.status.hasFocus = true;
@@ -640,7 +642,7 @@ Selection_.prototype = {
640
642
 
641
643
  let format = this.format.getLine(tempCon, null);
642
644
  if (format === this.format.getBlock(format, null)) {
643
- tempCon = tempCon || tempConCache;
645
+ tempCon ||= tempConCache;
644
646
  format = dom.utils.createElement(dom.query.getParentElement(tempCon, dom.check.isTableCell) ? 'DIV' : this.options.get('defaultLine'));
645
647
  tempCon.parentNode.insertBefore(format, tempCon);
646
648
  if (tempCon !== tempConCache) format.appendChild(tempCon);
@@ -55,7 +55,7 @@ UI.prototype = {
55
55
  * @param {__se__FrameContext|null} fc Frame context
56
56
  */
57
57
  setEditorStyle(style, fc) {
58
- fc = fc || this.frameContext;
58
+ fc ||= this.frameContext;
59
59
 
60
60
  const fo = fc.get('options');
61
61
  fo.set('editorStyle', style);
@@ -270,7 +270,7 @@ UI.prototype = {
270
270
  if (type) dom.utils.addClass(this.alertModal, `se-alert-${type}`);
271
271
 
272
272
  if (this._closeSignal) this._alertInner.addEventListener('click', this._closeListener[1]);
273
- if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
273
+ this._bindClose &&= this.eventManager.removeGlobalEvent(this._bindClose);
274
274
  this._bindClose = this.eventManager.addGlobalEvent('keydown', this._closeListener[0]);
275
275
 
276
276
  this._alertArea.style.display = 'block';
@@ -286,7 +286,7 @@ UI.prototype = {
286
286
  dom.utils.removeClass(this.alertModal, 'se-alert-*');
287
287
  this._alertArea.style.display = 'none';
288
288
  if (this._closeSignal) this._alertInner.removeEventListener('click', this._closeListener[1]);
289
- if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
289
+ this._bindClose &&= this.eventManager.removeGlobalEvent(this._bindClose);
290
290
  },
291
291
 
292
292
  /**
@@ -334,7 +334,13 @@ export const DEFAULTS = {
334
334
  * @property {number} [fullScreenOffset=0] - Offset applied when entering fullscreen mode.
335
335
  * @property {string} [previewTemplate=null] - Custom template for preview mode.
336
336
  * @property {string} [printTemplate=null] - Custom template for print mode.
337
- * @property {boolean} [componentAutoSelect=false] - Enables automatic selection of inserted components.
337
+ * @property {__se__ComponentInsertBehaviorType} [componentInsertBehavior="auto"] - Enables automatic selection of inserted components.
338
+ * - For inline components: places the cursor near the inserted component or selects it if no nearby range is available.
339
+ * - For block components: executes behavior based on `selectMode`:
340
+ * - `auto`: Move cursor to the next line if possible, otherwise select the component.
341
+ * - `select`: Always select the inserted component.
342
+ * - `line`: Move cursor to the next line if possible, or create a new line and move there.
343
+ * - `none`: Do nothing.
338
344
  * @property {string} [defaultUrlProtocol=null] - Default URL protocol for links.
339
345
  * @property {Object<"copy", number>} [toastMessageTime={copy: 1500}] - {"copy": 1500} - Duration for displaying toast messages.
340
346
  * @property {string} [freeCodeViewMode=false] - Enables free code view mode.
@@ -497,7 +503,7 @@ export const OPTION_FIXED_FLAG = {
497
503
  fullScreenOffset: true,
498
504
  previewTemplate: true,
499
505
  printTemplate: true,
500
- componentAutoSelect: true,
506
+ componentInsertBehavior: true,
501
507
  defaultUrlProtocol: true,
502
508
  allUsedStyles: 'fixed',
503
509
  toastMessageTime: true,
@@ -1050,7 +1050,7 @@ Editor.prototype = {
1050
1050
  */
1051
1051
  focusEdge(focusEl) {
1052
1052
  this._preventBlur = false;
1053
- if (!focusEl) focusEl = this.frameContext.get('wysiwyg').lastElementChild;
1053
+ focusEl ||= this.frameContext.get('wysiwyg').lastElementChild;
1054
1054
 
1055
1055
  const fileComponentInfo = this.component.get(focusEl);
1056
1056
  if (fileComponentInfo) {
@@ -1290,7 +1290,7 @@ Editor.prototype = {
1290
1290
  * @param {?__se__FrameContext=} fc - Frame context object, If null fc is this.frameContext
1291
1291
  */
1292
1292
  _checkPlaceholder(fc) {
1293
- fc = /** @type {__se__FrameContext} */ (fc || this.frameContext);
1293
+ fc ||= /** @type {__se__FrameContext} */ (this.frameContext);
1294
1294
  const placeholder = fc.get('placeholder');
1295
1295
 
1296
1296
  if (placeholder) {
@@ -122,7 +122,7 @@ export function SELECT_ALL(editor) {
122
122
 
123
123
  let info = null;
124
124
  if (dom.check.isMedia(first) || (info = editor.component.get(first.parentElement)) || dom.check.isTableElements(first)) {
125
- if (!info) info = editor.component.get(first);
125
+ info ||= editor.component.get(first);
126
126
  const br = dom.utils.createElement('BR');
127
127
  const format = dom.utils.createElement(editor.options.get('defaultLine'), null, br);
128
128
  first = info ? info.container || info.cover : first;
@@ -131,7 +131,7 @@ export function SELECT_ALL(editor) {
131
131
  }
132
132
 
133
133
  if (dom.check.isMedia(last) || (info = editor.component.get(last.parentElement)) || dom.check.isTableElements(last)) {
134
- if (!info) info = editor.component.get(first);
134
+ info ||= editor.component.get(first);
135
135
  const br = dom.utils.createElement('BR');
136
136
  const format = dom.utils.createElement(editor.options.get('defaultLine'), null, br);
137
137
  last = info ? info.container || info.cover : last;
@@ -259,7 +259,7 @@ export function FONT_STYLE(editor, command) {
259
259
  */
260
260
  export function PAGE_BREAK(editor) {
261
261
  const pageBreak = dom.utils.createElement('DIV', { class: 'se-component se-component-line-break se-page-break' });
262
- editor.component.insert(pageBreak, { skipCharCount: true, skipSelection: true, skipHistory: false });
262
+ editor.component.insert(pageBreak, { skipCharCount: true, insertBehavior: 'line' });
263
263
  const line = pageBreak.nextElementSibling || editor.format.addLine(pageBreak);
264
264
  editor.selection.setRange(line, 1, line, 1);
265
265
  editor.history.push(false);
@@ -258,7 +258,7 @@ export function CreateShortcuts(command, button, values, keyMap, rc, reverseKeys
258
258
  a = values[i].split('+');
259
259
 
260
260
  plugin = null;
261
- method = a[a.length - 1].trim?.();
261
+ method = a.at(-1).trim?.();
262
262
  if (method.startsWith('~')) {
263
263
  // plugin key, method
264
264
  plugin = command;
@@ -633,7 +633,7 @@ export function InitOptions(options, editorTargets, plugins) {
633
633
  o.set('printTemplate', typeof options.printTemplate === 'string' ? options.printTemplate : null);
634
634
 
635
635
  /** --- Media select */
636
- o.set('componentAutoSelect', options.componentAutoSelect === undefined ? false : !!options.componentAutoSelect);
636
+ o.set('componentInsertBehavior', ['auto', 'select', 'line', 'none'].includes(options.componentInsertBehavior) ? options.componentInsertBehavior : 'auto');
637
637
 
638
638
  /** --- Url input protocol */
639
639
  o.set('defaultUrlProtocol', typeof options.defaultUrlProtocol === 'string' ? options.defaultUrlProtocol : null);
@@ -719,7 +719,7 @@ export function CreateStatusbar(targetOptions, statusbar) {
719
719
  let charCounter = null;
720
720
 
721
721
  if (targetOptions.get('statusbar')) {
722
- statusbar = statusbar || dom.utils.createElement('DIV', { class: 'se-status-bar sun-editor-common' });
722
+ statusbar ||= dom.utils.createElement('DIV', { class: 'se-status-bar sun-editor-common' });
723
723
 
724
724
  /** navigation */
725
725
  navigation = statusbar.querySelector('.se-navigation') || dom.utils.createElement('DIV', { class: 'se-navigation sun-editor-common' });
@@ -1067,7 +1067,7 @@ function _createModuleGroup() {
1067
1067
  * @returns {{li: HTMLElement, button: HTMLElement}}
1068
1068
  */
1069
1069
  function _createButton(className, title, dataCommand, dataType, innerHTML, _disabled, icons) {
1070
- if (!innerHTML) innerHTML = '';
1070
+ innerHTML ||= '';
1071
1071
 
1072
1072
  const oLi = dom.utils.createElement('LI');
1073
1073
  const label = title || '';
@@ -44,14 +44,29 @@ export async function write(content) {
44
44
  plainText = content.textContent;
45
45
  }
46
46
 
47
- /* eslint-disable-next-line compat/compat */
48
- await navigator.clipboard.write([
47
+ try {
49
48
  /* eslint-disable-next-line compat/compat */
50
- new ClipboardItem({
51
- 'text/html': new Blob([htmlString], { type: 'text/html' }),
52
- 'text/plain': new Blob([plainText], { type: 'text/plain' })
53
- })
54
- ]);
49
+ await navigator.clipboard.write([
50
+ /* eslint-disable-next-line compat/compat */
51
+ new ClipboardItem({
52
+ 'text/html': new Blob([htmlString], { type: 'text/html' }),
53
+ 'text/plain': new Blob([plainText], { type: 'text/plain' })
54
+ })
55
+ ]);
56
+ } catch {
57
+ console.warn('[SUNEDITOR.copy.warn] This browser is not supported Clipboard API');
58
+ try {
59
+ await navigator.clipboard.writeText(plainText || stripHtml(htmlString));
60
+ } catch (err) {
61
+ console.error('[SUNEDITOR.copy.fail] ' + err);
62
+ }
63
+ }
64
+ }
65
+
66
+ function stripHtml(html) {
67
+ const div = document.createElement('div');
68
+ div.innerHTML = html;
69
+ return div.textContent || div.innerText || '';
55
70
  }
56
71
 
57
72
  export default {
@@ -112,11 +112,7 @@ export function getListChildren(element, validation) {
112
112
  const el = /** @type {Element} */ (element);
113
113
  if (!el.children || el.children.length === 0) return children;
114
114
 
115
- validation =
116
- validation ||
117
- function () {
118
- return true;
119
- };
115
+ validation ||= () => true;
120
116
 
121
117
  (function recursionFunc(current) {
122
118
  if (el !== current && validation(current)) {
@@ -144,11 +140,7 @@ export function getListChildNodes(element, validation) {
144
140
  const children = [];
145
141
  if (!element || element.childNodes.length === 0) return children;
146
142
 
147
- validation =
148
- validation ||
149
- function () {
150
- return true;
151
- };
143
+ validation ||= () => true;
152
144
 
153
145
  (function recursionFunc(current) {
154
146
  if (element !== current && validation(current)) {
@@ -275,7 +267,7 @@ export function getParentElement(element, query, depth) {
275
267
  valid = (el) => regExp.test(el[attr]);
276
268
  }
277
269
 
278
- if (!depth) depth = Infinity;
270
+ depth ||= Infinity;
279
271
  let index = 0;
280
272
  while (element && !valid(element)) {
281
273
  if (index >= depth || domCheck.isWysiwygFrame(element)) {
@@ -329,7 +321,7 @@ export function getParentElements(element, query, depth) {
329
321
  }
330
322
 
331
323
  const elementList = [];
332
- if (!depth) depth = Infinity;
324
+ depth ||= Infinity;
333
325
  let index = 0;
334
326
  while (index <= depth && element && !domCheck.isWysiwygFrame(element)) {
335
327
  if (valid(element)) {
@@ -426,7 +418,7 @@ export function getEdgeChild(node, query, last) {
426
418
  */
427
419
  export function getEdgeChildNodes(first, last) {
428
420
  if (!first) return;
429
- if (!last) last = first;
421
+ last ||= first;
430
422
 
431
423
  while (first && first.nodeType === 1 && first.childNodes.length > 0 && !domCheck.isBreak(first)) first = first.firstChild;
432
424
  while (last && last.nodeType === 1 && last.childNodes.length > 0 && !domCheck.isBreak(last)) last = last.lastChild;
@@ -501,7 +493,7 @@ export function getNextDeepestNode(node, ceiling) {
501
493
  */
502
494
  export function findTextIndexOnLine(line, offsetContainer, offset, validate) {
503
495
  if (!line) return 0;
504
- if (!validate) validate = () => true;
496
+ validate ||= () => true;
505
497
 
506
498
  let index = 0;
507
499
  let found = false;
@@ -584,7 +576,7 @@ export function findVisualLastCell(cells) {
584
576
  const colSpan = cell.colSpan || 1;
585
577
 
586
578
  // 현재 행에서 visual column index 찾기
587
- if (!occupied[rowIndex]) occupied[rowIndex] = [];
579
+ occupied[rowIndex] ||= [];
588
580
 
589
581
  let colIndex = 0;
590
582
  const rowOcc = occupied[rowIndex];
@@ -596,7 +588,7 @@ export function findVisualLastCell(cells) {
596
588
 
597
589
  for (let r = 1; r < rowSpan; r++) {
598
590
  const nextRow = rowIndex + r;
599
- if (!occupied[nextRow]) occupied[nextRow] = [];
591
+ occupied[nextRow] ||= [];
600
592
  for (let i = 0; i < colSpan; i++) {
601
593
  occupied[nextRow][colIndex + i] = true;
602
594
  }
@@ -123,11 +123,8 @@ export function getAttributesToString(element, exceptAttrs) {
123
123
  export function arrayFilter(array, validation) {
124
124
  if (!array || array.length === 0) return null;
125
125
 
126
- validation =
127
- validation ||
128
- function () {
129
- return true;
130
- };
126
+ validation ||= () => true;
127
+
131
128
  const arr = [];
132
129
 
133
130
  for (let i = 0, len = array.length, a; i < len; i++) {
@@ -149,11 +146,7 @@ export function arrayFilter(array, validation) {
149
146
  export function arrayFind(array, validation) {
150
147
  if (!array || array.length === 0) return null;
151
148
 
152
- validation =
153
- validation ||
154
- function () {
155
- return true;
156
- };
149
+ validation ||= () => true;
157
150
 
158
151
  for (let i = 0, len = array.length, a; i < len; i++) {
159
152
  a = array[i];
@@ -59,13 +59,13 @@ class ApiManager {
59
59
  call({ method, url, headers, data, callBack, errorCallBack, responseType }) {
60
60
  this.cancel();
61
61
 
62
- method = method || this.method;
62
+ method ||= this.method;
63
63
  url = this._normalizeUrl(url || this.url);
64
- headers = headers || this.headers;
65
- data = data || this.data;
66
- callBack = callBack || this.callBack;
67
- errorCallBack = errorCallBack || this.errorCallBack;
68
- responseType = responseType || this.responseType;
64
+ headers ||= this.headers;
65
+ data ||= this.data;
66
+ callBack ||= this.callBack;
67
+ errorCallBack ||= this.errorCallBack;
68
+ responseType ||= this.responseType;
69
69
 
70
70
  const xhr = this._xhr;
71
71
  if (responseType) xhr.responseType = responseType;
@@ -93,11 +93,11 @@ class ApiManager {
93
93
  asyncCall({ method, url, headers, data, responseType }) {
94
94
  this.cancel();
95
95
 
96
- method = method || this.method;
96
+ method ||= this.method;
97
97
  url = this._normalizeUrl(url || this.url);
98
- headers = headers || this.headers;
99
- data = data || this.data;
100
- responseType = responseType || this.responseType;
98
+ headers ||= this.headers;
99
+ data ||= this.data;
100
+ responseType ||= this.responseType;
101
101
 
102
102
  const xhr = this._xhr;
103
103
  if (responseType) xhr.responseType = responseType;
@@ -135,8 +135,7 @@ class Browser extends CoreInjector {
135
135
  * @param {string=} params.url - File server url. If not, use "this.url".
136
136
  * @param {Object<string, string>=} params.urlHeader - File server http header. If not, use "this.urlHeader".
137
137
  */
138
- open(params) {
139
- if (!params) params = {};
138
+ open(params = {}) {
140
139
  this.__addGlobalEvent();
141
140
 
142
141
  const listClassName = params.listClass || this.listClass;
@@ -297,7 +296,7 @@ class Browser extends CoreInjector {
297
296
  * @description Removes the global event listener for closing the browser.
298
297
  */
299
298
  __removeGlobalEvent() {
300
- if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
299
+ this._bindClose &&= this.eventManager.removeGlobalEvent(this._bindClose);
301
300
  }
302
301
 
303
302
  /**
@@ -354,9 +353,7 @@ class Browser extends CoreInjector {
354
353
  const parts = path.split('/');
355
354
  const len = parts.length - 1;
356
355
  parts.forEach((part, index) => {
357
- if (!current[part]) {
358
- current[part] = { children: {} };
359
- }
356
+ current[part] ||= { children: {} };
360
357
 
361
358
  if (index === len) {
362
359
  current[part].key = path;
@@ -305,7 +305,7 @@ class ColorPicker extends CoreInjector {
305
305
  * @returns
306
306
  */
307
307
  function CreateHTML({ lang, icons }, { colorList, disableHEXInput, disableRemove, splitNum }) {
308
- colorList = colorList || DEFAULT_COLOR_LIST;
308
+ colorList ||= DEFAULT_COLOR_LIST;
309
309
  splitNum = colorList === DEFAULT_COLOR_LIST ? DEFAULLT_COLOR_SPLITNUM : splitNum;
310
310
 
311
311
  let list = '';
@@ -339,8 +339,8 @@ class Controller extends EditorInjector {
339
339
  */
340
340
  __removeGlobalEvent() {
341
341
  this.component.__removeGlobalEvent();
342
- if (this._bindClose_key) this._bindClose_key = this.eventManager.removeGlobalEvent(this._bindClose_key);
343
- if (this._bindClose_mouse) this._bindClose_mouse = this.eventManager.removeGlobalEvent(this._bindClose_mouse);
342
+ this._bindClose_key &&= this.eventManager.removeGlobalEvent(this._bindClose_key);
343
+ this._bindClose_mouse &&= this.eventManager.removeGlobalEvent(this._bindClose_mouse);
344
344
  }
345
345
 
346
346
  /**
@@ -147,7 +147,6 @@ class Figure extends EditorInjector {
147
147
  this.__onContainerEvent = null;
148
148
  this.__offContainerEvent = null;
149
149
  this.__onResizeESCEvent = null;
150
- this.__fileManagerInfo = false;
151
150
 
152
151
  // init
153
152
  this.eventManager.addEvent(this.alignButton, 'click', this.#OnClick_alignButton.bind(this));
@@ -213,10 +212,10 @@ class Figure extends EditorInjector {
213
212
  const inlineCover = dom.query.getParentElement(element, 'SPAN', 2);
214
213
  return {
215
214
  target: /** @type {HTMLElement} */ (element),
216
- container: /** @type {HTMLElement} */ (dom.query.getParentElement(element, Figure.is, 2) || cover),
217
- cover: /** @type {HTMLElement} */ (cover),
215
+ container: dom.query.getParentElement(element, Figure.is, 2) || cover,
216
+ cover: cover,
218
217
  inlineCover: dom.utils.hasClass(inlineCover, 'se-inline-component') ? /** @type {HTMLElement} */ (inlineCover) : null,
219
- caption: /** @type {HTMLElement} */ (dom.query.getEdgeChild(element.parentElement, 'FIGCAPTION', false)),
218
+ caption: dom.query.getEdgeChild(element.parentElement, 'FIGCAPTION', false),
220
219
  isVertical: IsVertical(element)
221
220
  };
222
221
  }
@@ -302,10 +301,10 @@ class Figure extends EditorInjector {
302
301
  * @param {boolean} [params.nonSizeInfo=false] Do not display the size information
303
302
  * @param {boolean} [params.nonBorder=false] Do not display the selected style line
304
303
  * @param {boolean} [params.figureTarget=false] If true, the target is a figure element
305
- * @param {boolean} [params.__fileManagerInfo=false] If true, the file manager is called
304
+ * @param {boolean} [params.infoOnly=false] If true, returns only the figure target info without opening the controller
306
305
  * @returns {FigureTargetInfo|undefined} figure target info
307
306
  */
308
- open(targetNode, { nonResizing, nonSizeInfo, nonBorder, figureTarget, __fileManagerInfo }) {
307
+ open(targetNode, { nonResizing, nonSizeInfo, nonBorder, figureTarget, infoOnly }) {
309
308
  if (!targetNode) {
310
309
  console.warn('[SUNEDITOR.modules.Figure.open] The "targetNode" is null.');
311
310
  return;
@@ -369,7 +368,7 @@ class Figure extends EditorInjector {
369
368
 
370
369
  this._width = targetInfo.width;
371
370
  this._height = targetInfo.height;
372
- if (__fileManagerInfo || this.__fileManagerInfo) return targetInfo;
371
+ if (infoOnly) return targetInfo;
373
372
 
374
373
  const _figure = this.frameContext.get('_figure');
375
374
  this.editor._figureContainer = _figure.main;
@@ -522,7 +521,7 @@ class Figure extends EditorInjector {
522
521
  * @returns {{w: string, h: string, dw: string, dh: string}}
523
522
  */
524
523
  getSize(targetNode) {
525
- if (!targetNode) targetNode = this._element;
524
+ targetNode ||= this._element;
526
525
 
527
526
  const v = IsVertical(targetNode);
528
527
  let w = '';
@@ -547,13 +546,14 @@ class Figure extends EditorInjector {
547
546
  w = !/%$/.test(target.style.width) ? target.style.width : ((figure.container && numbers.get(figure.container.style.width, 2)) || 100) + '%';
548
547
  h = figure.inlineCover
549
548
  ? figure.inlineCover.style.height || /** @type {HTMLElement} */ (targetNode).style.height || String(/** @type {HTMLImageElement} */ (targetNode).height || '')
550
- : numbers.get(figure.cover.style.paddingBottom, 0) > 0 && !v
551
- ? figure.cover.style.height
549
+ : numbers.get(figure.cover?.style.paddingBottom, 0) > 0 && !v
550
+ ? figure.cover?.style.height
552
551
  : !/%$/.test(target.style.height) || !/%$/.test(target.style.width)
553
552
  ? target.style.height
554
553
  : ((figure.container && numbers.get(figure.container.style.height, 2)) || 100) + '%';
555
- w = w || 'auto';
556
- h = h || 'auto';
554
+
555
+ w ||= 'auto';
556
+ h ||= 'auto';
557
557
  }
558
558
 
559
559
  dw = v ? h : w;
@@ -573,8 +573,8 @@ class Figure extends EditorInjector {
573
573
  * @param {string} align "none"|"left"|"center"|"right"
574
574
  */
575
575
  setAlign(targetNode, align) {
576
- if (!targetNode) targetNode = this._element;
577
- this.align = align = align || 'none';
576
+ targetNode ||= this._element;
577
+ this.align = align ||= 'none';
578
578
 
579
579
  const figure = Figure.GetContainer(targetNode);
580
580
  if (!figure.cover) return;
@@ -610,7 +610,7 @@ class Figure extends EditorInjector {
610
610
  * @returns {HTMLElement} New target element after conversion
611
611
  */
612
612
  convertAsFormat(targetNode, formatStyle) {
613
- if (!targetNode) targetNode = this._element;
613
+ targetNode ||= this._element;
614
614
  this.as = formatStyle || 'block';
615
615
  const { container, inlineCover, target } = Figure.GetContainer(targetNode);
616
616
  const { w, h } = this.getSize(target);
@@ -798,7 +798,12 @@ class Figure extends EditorInjector {
798
798
  */
799
799
  retainFigureFormat(container, originEl, anchorCover, fileManagerInst) {
800
800
  const isInline = this.component.isInline(container);
801
- 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;
801
+ let existElement =
802
+ this.format.isBlock(originEl.parentNode) || dom.check.isWysiwygFrame(originEl.parentNode)
803
+ ? originEl
804
+ : dom.check.isAnchor(originEl.parentNode)
805
+ ? originEl.parentNode
806
+ : Figure.GetContainer(originEl)?.container || this.format.getLine(originEl) || originEl;
802
807
 
803
808
  if (dom.query.getParentElement(originEl, dom.check.isExcludeFormat)) {
804
809
  existElement = anchorCover && anchorCover !== originEl ? anchorCover : originEl;
@@ -835,7 +840,7 @@ class Figure extends EditorInjector {
835
840
  * @param {?Node=} node Target element, default is the current element
836
841
  */
837
842
  deleteTransform(node) {
838
- if (!node) node = this._element;
843
+ node ||= this._element;
839
844
 
840
845
  const element = /** @type {HTMLElement} */ (node);
841
846
  const size = (element.getAttribute('data-se-size') || '').split(',');
@@ -889,8 +894,10 @@ class Figure extends EditorInjector {
889
894
  this._deletePercentSize();
890
895
  this._applySize(offsetW + 'px', offsetH + 'px', '');
891
896
 
892
- cover.style.width = w;
893
- cover.style.height = figureInfo.caption || figureInfo.inlineCover ? '' : h;
897
+ if (cover) {
898
+ cover.style.width = w;
899
+ cover.style.height = figureInfo.caption || figureInfo.inlineCover ? '' : h;
900
+ }
894
901
 
895
902
  if (isVertical) {
896
903
  const transW = offsetW / 2 + 'px ' + offsetW / 2 + 'px 0';
@@ -986,14 +993,14 @@ class Figure extends EditorInjector {
986
993
 
987
994
  const sizeTarget = this._cover || this._element;
988
995
 
989
- if (this.autoRatio) this._cover.style.width = w;
996
+ if (this.autoRatio && this._cover) this._cover.style.width = w;
990
997
  if (!onlyH) {
991
998
  sizeTarget.style.width = this._element.style.width = w;
992
999
  }
993
1000
  if (!onlyW) {
994
1001
  h = numbers.is(h) ? h + this.sizeUnit : h;
995
1002
  sizeTarget.style.height = this._element.style.height = this.autoRatio && !this.isVertical ? '100%' : h;
996
- if (this.autoRatio) {
1003
+ if (this.autoRatio && this._cover) {
997
1004
  this._cover.style.height = h;
998
1005
  this.__setCoverPaddingBottom(w, h);
999
1006
  }
@@ -1041,8 +1048,10 @@ class Figure extends EditorInjector {
1041
1048
  this._element.style.maxWidth = '';
1042
1049
  this._element.style.width = '';
1043
1050
  this._element.style.height = '';
1044
- this._cover.style.width = '';
1045
- this._cover.style.height = '';
1051
+ if (this._cover) {
1052
+ this._cover.style.width = '';
1053
+ this._cover.style.height = '';
1054
+ }
1046
1055
  }
1047
1056
 
1048
1057
  this.setAlign(this._element, this.align);
@@ -1058,7 +1067,7 @@ class Figure extends EditorInjector {
1058
1067
  * @param {string|number} h Height percentage.
1059
1068
  */
1060
1069
  _setPercentSize(w, h) {
1061
- if (!h) h = this.autoRatio ? (/%$/.test(this.autoRatio.current) ? this.autoRatio.current : this.autoRatio.default) : h;
1070
+ h ||= this.autoRatio ? (/%$/.test(this.autoRatio.current) ? this.autoRatio.current : this.autoRatio.default) : h;
1062
1071
  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 : '');
1063
1072
 
1064
1073
  const heightPercentage = /%$/.test(h + '');
@@ -1071,10 +1080,11 @@ class Figure extends EditorInjector {
1071
1080
  return;
1072
1081
  }
1073
1082
 
1074
- if (this._inlineCover !== this._cover) {
1083
+ if (this._inlineCover !== this._cover && this._cover) {
1075
1084
  this._cover.style.width = '100%';
1076
1085
  this._cover.style.height = String(h);
1077
1086
  }
1087
+
1078
1088
  this._element.style.width = '100%';
1079
1089
  this._element.style.maxWidth = '';
1080
1090
  this._element.style.height = String(this.autoRatio ? '100%' : heightPercentage ? '' : h);
@@ -1095,8 +1105,11 @@ class Figure extends EditorInjector {
1095
1105
  * @description Deletes percentage-based sizing from the figure element.
1096
1106
  */
1097
1107
  _deletePercentSize() {
1098
- this._cover.style.width = '';
1099
- this._cover.style.height = '';
1108
+ if (this._cover) {
1109
+ this._cover.style.width = '';
1110
+ this._cover.style.height = '';
1111
+ }
1112
+
1100
1113
  this._container.style.width = '';
1101
1114
  this._container.style.height = '';
1102
1115
 
@@ -1161,7 +1174,12 @@ class Figure extends EditorInjector {
1161
1174
  _setCaptionPosition(element) {
1162
1175
  const figcaption = /** @type {HTMLElement} */ (dom.query.getEdgeChild(dom.query.getParentElement(element, 'FIGURE'), 'FIGCAPTION', false));
1163
1176
  if (figcaption) {
1164
- figcaption.style.marginTop = (this.isVertical ? element.offsetWidth - element.offsetHeight : 0) + 'px';
1177
+ figcaption.style.marginTop = (this.isVertical && !this.autoRatio ? element.offsetWidth - element.offsetHeight : 0) + 'px';
1178
+ if (this.isVertical && this.autoRatio) {
1179
+ element.style.marginTop = figcaption.offsetHeight + 'px';
1180
+ } else {
1181
+ element.style.marginTop = '';
1182
+ }
1165
1183
  }
1166
1184
  }
1167
1185
 
@@ -139,12 +139,10 @@ class FileManager extends CoreInjector {
139
139
  let info = null;
140
140
  let state = '';
141
141
 
142
- if (!file) {
143
- file = {
144
- name: GetAttr(element, 'file-name') || (typeof element.src === 'string' ? element.src.split('/').pop() : ''),
145
- size: Number(GetAttr(element, 'file-size')) || 0
146
- };
147
- }
142
+ file ||= {
143
+ name: GetAttr(element, 'file-name') || (typeof element.src === 'string' ? element.src.split('/').pop() : ''),
144
+ size: Number(GetAttr(element, 'file-size')) || 0
145
+ };
148
146
 
149
147
  // create
150
148
  if (!dataIndex || this.editor._componentsInfoInit) {