suneditor 3.0.0-alpha.9 → 3.0.0-beta.2

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 (315) hide show
  1. package/CONTRIBUTING.md +170 -22
  2. package/{LICENSE.txt → LICENSE} +9 -9
  3. package/README.md +168 -30
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +47 -21
  7. package/src/assets/design/color.css +121 -0
  8. package/src/assets/design/index.css +3 -0
  9. package/src/assets/design/size.css +35 -0
  10. package/src/assets/design/typography.css +37 -0
  11. package/src/assets/icons/defaultIcons.js +232 -0
  12. package/src/assets/suneditor-contents.css +181 -46
  13. package/src/assets/suneditor.css +1403 -650
  14. package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +23 -4
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +49 -10
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +422 -224
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +83 -36
  19. package/src/core/base/eventManager.js +520 -179
  20. package/src/core/base/history.js +95 -41
  21. package/src/core/class/char.js +26 -11
  22. package/src/core/class/component.js +345 -137
  23. package/src/core/class/format.js +683 -519
  24. package/src/core/class/html.js +485 -305
  25. package/src/core/class/menu.js +133 -47
  26. package/src/core/class/nodeTransform.js +90 -71
  27. package/src/core/class/offset.js +408 -92
  28. package/src/core/class/selection.js +216 -106
  29. package/src/core/class/shortcuts.js +68 -8
  30. package/src/core/class/toolbar.js +106 -116
  31. package/src/core/class/ui.js +422 -0
  32. package/src/core/class/viewer.js +178 -74
  33. package/src/core/editor.js +496 -389
  34. package/src/core/section/actives.js +123 -27
  35. package/src/core/section/constructor.js +615 -206
  36. package/src/core/section/context.js +28 -23
  37. package/src/core/section/documentType.js +561 -0
  38. package/src/editorInjector/_classes.js +19 -5
  39. package/src/editorInjector/_core.js +71 -7
  40. package/src/editorInjector/index.js +63 -1
  41. package/src/events.js +622 -0
  42. package/src/helper/clipboard.js +59 -0
  43. package/src/helper/converter.js +202 -26
  44. package/src/helper/dom/domCheck.js +304 -0
  45. package/src/helper/dom/domQuery.js +669 -0
  46. package/src/helper/dom/domUtils.js +557 -0
  47. package/src/helper/dom/index.js +12 -0
  48. package/src/helper/env.js +46 -56
  49. package/src/helper/index.js +10 -4
  50. package/src/helper/keyCodeMap.js +183 -0
  51. package/src/helper/numbers.js +12 -8
  52. package/src/helper/unicode.js +9 -5
  53. package/src/langs/ckb.js +74 -4
  54. package/src/langs/cs.js +72 -2
  55. package/src/langs/da.js +73 -3
  56. package/src/langs/de.js +73 -4
  57. package/src/langs/en.js +23 -3
  58. package/src/langs/es.js +73 -4
  59. package/src/langs/fa.js +75 -3
  60. package/src/langs/fr.js +73 -3
  61. package/src/langs/he.js +73 -4
  62. package/src/langs/hu.js +230 -0
  63. package/src/langs/index.js +7 -3
  64. package/src/langs/it.js +70 -1
  65. package/src/langs/ja.js +72 -4
  66. package/src/langs/km.js +230 -0
  67. package/src/langs/ko.js +22 -2
  68. package/src/langs/lv.js +74 -5
  69. package/src/langs/nl.js +73 -4
  70. package/src/langs/pl.js +73 -4
  71. package/src/langs/pt_br.js +70 -1
  72. package/src/langs/ro.js +74 -5
  73. package/src/langs/ru.js +73 -4
  74. package/src/langs/se.js +73 -4
  75. package/src/langs/tr.js +73 -1
  76. package/src/langs/{ua.js → uk.js} +75 -6
  77. package/src/langs/ur.js +77 -8
  78. package/src/langs/zh_cn.js +74 -5
  79. package/src/modules/ApiManager.js +77 -54
  80. package/src/modules/Browser.js +667 -0
  81. package/src/modules/ColorPicker.js +162 -102
  82. package/src/modules/Controller.js +273 -142
  83. package/src/modules/Figure.js +925 -484
  84. package/src/modules/FileManager.js +121 -69
  85. package/src/modules/HueSlider.js +113 -61
  86. package/src/modules/Modal.js +291 -122
  87. package/src/modules/ModalAnchorEditor.js +383 -234
  88. package/src/modules/SelectMenu.js +270 -168
  89. package/src/modules/_DragHandle.js +2 -1
  90. package/src/modules/index.js +3 -3
  91. package/src/plugins/browser/audioGallery.js +83 -0
  92. package/src/plugins/browser/fileBrowser.js +103 -0
  93. package/src/plugins/browser/fileGallery.js +83 -0
  94. package/src/plugins/browser/imageGallery.js +81 -0
  95. package/src/plugins/browser/videoGallery.js +103 -0
  96. package/src/plugins/command/blockquote.js +40 -27
  97. package/src/plugins/command/exportPDF.js +134 -0
  98. package/src/plugins/command/fileUpload.js +229 -162
  99. package/src/plugins/command/list_bulleted.js +83 -47
  100. package/src/plugins/command/list_numbered.js +83 -47
  101. package/src/plugins/dropdown/align.js +66 -54
  102. package/src/plugins/dropdown/backgroundColor.js +63 -49
  103. package/src/plugins/dropdown/font.js +71 -47
  104. package/src/plugins/dropdown/fontColor.js +63 -48
  105. package/src/plugins/dropdown/formatBlock.js +70 -33
  106. package/src/plugins/dropdown/hr.js +92 -51
  107. package/src/plugins/dropdown/layout.js +37 -26
  108. package/src/plugins/dropdown/lineHeight.js +54 -38
  109. package/src/plugins/dropdown/list.js +60 -45
  110. package/src/plugins/dropdown/paragraphStyle.js +51 -30
  111. package/src/plugins/dropdown/table.js +2003 -813
  112. package/src/plugins/dropdown/template.js +38 -26
  113. package/src/plugins/dropdown/textStyle.js +43 -31
  114. package/src/plugins/field/mention.js +147 -86
  115. package/src/plugins/index.js +32 -6
  116. package/src/plugins/input/fontSize.js +161 -108
  117. package/src/plugins/input/pageNavigator.js +70 -0
  118. package/src/plugins/modal/audio.js +358 -173
  119. package/src/plugins/modal/drawing.js +531 -0
  120. package/src/plugins/modal/embed.js +886 -0
  121. package/src/plugins/modal/image.js +674 -362
  122. package/src/plugins/modal/link.js +100 -71
  123. package/src/plugins/modal/math.js +367 -167
  124. package/src/plugins/modal/video.js +691 -335
  125. package/src/plugins/popup/anchor.js +222 -0
  126. package/src/suneditor.js +50 -13
  127. package/src/themes/dark.css +122 -0
  128. package/src/typedef.js +130 -0
  129. package/types/assets/icons/defaultIcons.d.ts +153 -0
  130. package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
  131. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
  132. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
  133. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
  134. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
  135. package/types/core/base/eventManager.d.ts +385 -0
  136. package/types/core/base/history.d.ts +81 -0
  137. package/types/core/class/char.d.ts +60 -0
  138. package/types/core/class/component.d.ts +212 -0
  139. package/types/core/class/format.d.ts +616 -0
  140. package/types/core/class/html.d.ts +422 -0
  141. package/types/core/class/menu.d.ts +126 -0
  142. package/types/core/class/nodeTransform.d.ts +93 -0
  143. package/types/core/class/offset.d.ts +522 -0
  144. package/types/core/class/selection.d.ts +188 -0
  145. package/types/core/class/shortcuts.d.ts +142 -0
  146. package/types/core/class/toolbar.d.ts +189 -0
  147. package/types/core/class/ui.d.ts +164 -0
  148. package/types/core/class/viewer.d.ts +140 -0
  149. package/types/core/editor.d.ts +610 -0
  150. package/types/core/section/actives.d.ts +46 -0
  151. package/types/core/section/constructor.d.ts +777 -0
  152. package/types/core/section/context.d.ts +45 -0
  153. package/types/core/section/documentType.d.ts +178 -0
  154. package/types/editorInjector/_classes.d.ts +41 -0
  155. package/types/editorInjector/_core.d.ts +92 -0
  156. package/types/editorInjector/index.d.ts +71 -0
  157. package/types/events.d.ts +273 -0
  158. package/types/helper/clipboard.d.ts +12 -0
  159. package/types/helper/converter.d.ts +197 -0
  160. package/types/helper/dom/domCheck.d.ts +189 -0
  161. package/types/helper/dom/domQuery.d.ts +223 -0
  162. package/types/helper/dom/domUtils.d.ts +226 -0
  163. package/types/helper/dom/index.d.ts +9 -0
  164. package/types/helper/env.d.ts +132 -0
  165. package/types/helper/index.d.ts +174 -0
  166. package/types/helper/keyCodeMap.d.ts +110 -0
  167. package/types/helper/numbers.d.ts +46 -0
  168. package/types/helper/unicode.d.ts +28 -0
  169. package/types/index.d.ts +120 -0
  170. package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +173 -103
  171. package/types/langs/ckb.d.ts +3 -0
  172. package/types/langs/cs.d.ts +3 -0
  173. package/types/langs/da.d.ts +3 -0
  174. package/types/langs/de.d.ts +3 -0
  175. package/types/langs/en.d.ts +3 -0
  176. package/types/langs/es.d.ts +3 -0
  177. package/types/langs/fa.d.ts +3 -0
  178. package/types/langs/fr.d.ts +3 -0
  179. package/types/langs/he.d.ts +3 -0
  180. package/types/langs/hu.d.ts +3 -0
  181. package/types/langs/index.d.ts +54 -0
  182. package/types/langs/it.d.ts +3 -0
  183. package/types/langs/ja.d.ts +3 -0
  184. package/types/langs/km.d.ts +3 -0
  185. package/types/langs/ko.d.ts +3 -0
  186. package/types/langs/lv.d.ts +3 -0
  187. package/types/langs/nl.d.ts +3 -0
  188. package/types/langs/pl.d.ts +3 -0
  189. package/types/langs/pt_br.d.ts +3 -0
  190. package/types/langs/ro.d.ts +3 -0
  191. package/types/langs/ru.d.ts +3 -0
  192. package/types/langs/se.d.ts +3 -0
  193. package/types/langs/tr.d.ts +3 -0
  194. package/types/langs/uk.d.ts +3 -0
  195. package/types/langs/ur.d.ts +3 -0
  196. package/types/langs/zh_cn.d.ts +3 -0
  197. package/types/modules/ApiManager.d.ts +125 -0
  198. package/types/modules/Browser.d.ts +326 -0
  199. package/types/modules/ColorPicker.d.ts +131 -0
  200. package/types/modules/Controller.d.ts +251 -0
  201. package/types/modules/Figure.d.ts +517 -0
  202. package/types/modules/FileManager.d.ts +202 -0
  203. package/types/modules/HueSlider.d.ts +136 -0
  204. package/types/modules/Modal.d.ts +111 -0
  205. package/types/modules/ModalAnchorEditor.d.ts +236 -0
  206. package/types/modules/SelectMenu.d.ts +194 -0
  207. package/types/modules/_DragHandle.d.ts +7 -0
  208. package/types/modules/index.d.ts +26 -0
  209. package/types/plugins/browser/audioGallery.d.ts +55 -0
  210. package/types/plugins/browser/fileBrowser.d.ts +64 -0
  211. package/types/plugins/browser/fileGallery.d.ts +55 -0
  212. package/types/plugins/browser/imageGallery.d.ts +51 -0
  213. package/types/plugins/browser/videoGallery.d.ts +57 -0
  214. package/types/plugins/command/blockquote.d.ts +28 -0
  215. package/types/plugins/command/exportPDF.d.ts +46 -0
  216. package/types/plugins/command/fileUpload.d.ts +156 -0
  217. package/types/plugins/command/list_bulleted.d.ts +46 -0
  218. package/types/plugins/command/list_numbered.d.ts +46 -0
  219. package/types/plugins/dropdown/align.d.ts +60 -0
  220. package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
  221. package/types/plugins/dropdown/font.d.ts +54 -0
  222. package/types/plugins/dropdown/fontColor.d.ts +63 -0
  223. package/types/plugins/dropdown/formatBlock.d.ts +54 -0
  224. package/types/plugins/dropdown/hr.d.ts +71 -0
  225. package/types/plugins/dropdown/layout.d.ts +40 -0
  226. package/types/plugins/dropdown/lineHeight.d.ts +50 -0
  227. package/types/plugins/dropdown/list.d.ts +39 -0
  228. package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
  229. package/types/plugins/dropdown/table.d.ts +627 -0
  230. package/types/plugins/dropdown/template.d.ts +40 -0
  231. package/types/plugins/dropdown/textStyle.d.ts +41 -0
  232. package/types/plugins/field/mention.d.ts +102 -0
  233. package/types/plugins/index.d.ts +107 -0
  234. package/types/plugins/input/fontSize.d.ts +170 -0
  235. package/types/plugins/input/pageNavigator.d.ts +28 -0
  236. package/types/plugins/modal/audio.d.ts +269 -0
  237. package/types/plugins/modal/drawing.d.ts +246 -0
  238. package/types/plugins/modal/embed.d.ts +387 -0
  239. package/types/plugins/modal/image.d.ts +451 -0
  240. package/types/plugins/modal/link.d.ts +128 -0
  241. package/types/plugins/modal/math.d.ts +193 -0
  242. package/types/plugins/modal/video.d.ts +485 -0
  243. package/types/plugins/popup/anchor.d.ts +56 -0
  244. package/types/suneditor.d.ts +51 -0
  245. package/types/typedef.d.ts +233 -0
  246. package/.eslintignore +0 -7
  247. package/.eslintrc.json +0 -64
  248. package/src/assets/icons/_default.js +0 -194
  249. package/src/core/base/events.js +0 -320
  250. package/src/core/class/notice.js +0 -42
  251. package/src/helper/domUtils.js +0 -1177
  252. package/src/modules/FileBrowser.js +0 -271
  253. package/src/plugins/command/exportPdf.js +0 -168
  254. package/src/plugins/fileBrowser/imageGallery.js +0 -81
  255. package/src/themes/test.css +0 -61
  256. package/typings/CommandPlugin.d.ts +0 -8
  257. package/typings/DialogPlugin.d.ts +0 -20
  258. package/typings/FileBrowserPlugin.d.ts +0 -30
  259. package/typings/Module.d.ts +0 -15
  260. package/typings/Plugin.d.ts +0 -42
  261. package/typings/SubmenuPlugin.d.ts +0 -8
  262. package/typings/_classes.d.ts +0 -17
  263. package/typings/_colorPicker.d.ts +0 -60
  264. package/typings/_core.d.ts +0 -55
  265. package/typings/align.d.ts +0 -5
  266. package/typings/audio.d.ts +0 -5
  267. package/typings/backgroundColor.d.ts +0 -5
  268. package/typings/blockquote.d.ts +0 -5
  269. package/typings/char.d.ts +0 -39
  270. package/typings/component.d.ts +0 -38
  271. package/typings/context.d.ts +0 -39
  272. package/typings/converter.d.ts +0 -33
  273. package/typings/dialog.d.ts +0 -28
  274. package/typings/domUtils.d.ts +0 -361
  275. package/typings/editor.d.ts +0 -7
  276. package/typings/editor.ts +0 -542
  277. package/typings/env.d.ts +0 -70
  278. package/typings/eventManager.d.ts +0 -37
  279. package/typings/events.d.ts +0 -262
  280. package/typings/fileBrowser.d.ts +0 -42
  281. package/typings/fileManager.d.ts +0 -67
  282. package/typings/font.d.ts +0 -5
  283. package/typings/fontColor.d.ts +0 -5
  284. package/typings/fontSize.d.ts +0 -5
  285. package/typings/format.d.ts +0 -191
  286. package/typings/formatBlock.d.ts +0 -5
  287. package/typings/history.d.ts +0 -48
  288. package/typings/horizontalRule.d.ts +0 -5
  289. package/typings/image.d.ts +0 -5
  290. package/typings/imageGallery.d.ts +0 -5
  291. package/typings/index.d.ts +0 -21
  292. package/typings/index.modules.d.ts +0 -11
  293. package/typings/index.plugins.d.ts +0 -58
  294. package/typings/lineHeight.d.ts +0 -5
  295. package/typings/link.d.ts +0 -5
  296. package/typings/list.d.ts +0 -5
  297. package/typings/math.d.ts +0 -5
  298. package/typings/mediaContainer.d.ts +0 -25
  299. package/typings/mention.d.ts +0 -5
  300. package/typings/node.d.ts +0 -57
  301. package/typings/notice.d.ts +0 -16
  302. package/typings/numbers.d.ts +0 -29
  303. package/typings/offset.d.ts +0 -24
  304. package/typings/options.d.ts +0 -589
  305. package/typings/paragraphStyle.d.ts +0 -5
  306. package/typings/resizing.d.ts +0 -141
  307. package/typings/selection.d.ts +0 -94
  308. package/typings/shortcuts.d.ts +0 -13
  309. package/typings/suneditor.d.ts +0 -9
  310. package/typings/table.d.ts +0 -5
  311. package/typings/template.d.ts +0 -5
  312. package/typings/textStyle.d.ts +0 -5
  313. package/typings/toolbar.d.ts +0 -32
  314. package/typings/unicode.d.ts +0 -25
  315. package/typings/video.d.ts +0 -5
@@ -1,5 +1,5 @@
1
1
  import EditorInjector from '../../editorInjector';
2
- import { domUtils, numbers, converter, env } from '../../helper';
2
+ import { dom, numbers, converter, env, keyCodeMap } from '../../helper';
3
3
  import { Controller, SelectMenu, ColorPicker, Figure, _DragHandle } from '../../modules';
4
4
 
5
5
  const { _w, ON_OVER_COMPONENT } = env;
@@ -61,270 +61,420 @@ const DEFAULT_COLOR_LIST = [
61
61
  '#000000'
62
62
  ];
63
63
 
64
- const Table = function (editor, pluginOptions) {
65
- // plugin bisic properties
66
- EditorInjector.call(this, editor);
67
- this.title = this.lang.table;
68
- this.icon = 'table';
69
-
70
- // pluginOptions options
71
- this.figureScrollList = ['se-scroll-figure-xy', 'se-scroll-figure-x', 'se-scroll-figure-y'];
72
- this.figureScroll = typeof pluginOptions.scrollType === 'string' ? pluginOptions.scrollType.toLowerCase() : 'x';
73
- this.captionPosition = pluginOptions.captionPosition !== 'bottom' ? 'top' : 'bottom';
74
- this.cellControllerTop = (pluginOptions.cellControllerPosition !== 'cell' ? 'table' : 'cell') === 'table';
75
-
76
- // create HTML
77
- const menu = CreateHTML(editor);
78
- const commandArea = menu.querySelector('.se-controller-table-picker');
79
- const controller_table = CreateHTML_controller_table(editor);
80
- const controller_cell = CreateHTML_controller_cell(editor, this.cellControllerTop);
81
- const controller_props = CreateHTML_controller_properties(editor);
82
-
83
- editor.applyFrameRoots((e) => {
84
- e.get('wrapper').appendChild(domUtils.createElement('DIV', { class: RESIZE_CELL_CLASS.replace(/^\./, '') }));
85
- e.get('wrapper').appendChild(domUtils.createElement('DIV', { class: RESIZE_CELL_PREV_CLASS.replace(/^\./, '') }));
86
- e.get('wrapper').appendChild(domUtils.createElement('DIV', { class: RESIZE_ROW_CLASS.replace(/^\./, '') }));
87
- e.get('wrapper').appendChild(domUtils.createElement('DIV', { class: RESIZE_ROW_PREV_CLASS.replace(/^\./, '') }));
88
- });
89
-
90
- // members - Controller
91
- this.controller_table = new Controller(this, controller_table, { position: 'top' });
92
- this.controller_cell = new Controller(this, controller_cell, { position: this.cellControllerTop ? 'top' : 'bottom' });
93
- // props
94
- const propsTargetForms = [this.controller_table.form, this.controller_cell.form];
95
- this.controller_props = new Controller(this, controller_props, { position: 'bottom', parents: propsTargetForms, isInsideForm: true });
96
- this.controller_props_title = controller_props.querySelector('.se-controller-title');
97
- // color picker
98
- const colorForm = domUtils.createElement('DIV', { class: 'se-controller se-list-layer' }, null);
99
- this.colorPicker = new ColorPicker(this, '', {
100
- colorList: pluginOptions.colorList || DEFAULT_COLOR_LIST,
101
- splitNum: 5,
102
- disableRemove: true,
103
- hueSliderOptions: { controllerOptions: { parents: [colorForm], isOutsideForm: true } }
104
- });
64
+ /**
65
+ * @class
66
+ * @description Table Plugin
67
+ */
68
+ class Table extends EditorInjector {
69
+ static key = 'table';
70
+ static type = 'dropdown-free';
71
+ static className = '';
72
+ static options = { isInputComponent: true };
73
+ /**
74
+ * @this {Table}
75
+ * @param {HTMLElement} node - The node to check.
76
+ * @returns {HTMLElement|null} Returns a node if the node is a valid component.
77
+ */
78
+ static component(node) {
79
+ return dom.check.isTable(node) ? node : null;
80
+ }
105
81
 
106
- colorForm.appendChild(this.colorPicker.target);
107
- this.controller_colorPicker = new Controller(this, colorForm, {
108
- position: 'bottom',
109
- parents: [this.controller_props.form].concat(propsTargetForms),
110
- isInsideForm: true,
111
- isWWTarget: false,
112
- initMethod: () => {
113
- this.colorPicker.hueSlider.close();
114
- domUtils.removeClass(this.controller_colorPicker.currentTarget, 'on');
82
+ /**
83
+ * @constructor
84
+ * @param {__se__EditorCore} editor - The root editor instance
85
+ * @param {Object} pluginOptions
86
+ * @param {"x"|"y"|"xy"} [pluginOptions.scrollType='x'] - Scroll type ('x', 'y', 'xy')
87
+ * @param {"top"|"bottom"} [pluginOptions.captionPosition='bottom'] - Caption position ('top', 'bottom')
88
+ * @param {"cell"|"table"} [pluginOptions.cellControllerPosition='cell'] - Cell controller position ('cell', 'table')
89
+ * @param {Array} [pluginOptions.colorList] - Color list, used in cell color picker
90
+ */
91
+ constructor(editor, pluginOptions) {
92
+ // plugin bisic properties
93
+ super(editor);
94
+ this.title = this.lang.table;
95
+ this.icon = 'table';
96
+
97
+ // pluginOptions options
98
+ this.figureScrollList = ['se-scroll-figure-xy', 'se-scroll-figure-x', 'se-scroll-figure-y'];
99
+ this.figureScroll = typeof pluginOptions.scrollType === 'string' ? pluginOptions.scrollType.toLowerCase() : 'x';
100
+ this.captionPosition = pluginOptions.captionPosition !== 'bottom' ? 'top' : 'bottom';
101
+ this.cellControllerTop = (pluginOptions.cellControllerPosition !== 'cell' ? 'table' : 'cell') === 'table';
102
+
103
+ // create HTML
104
+ const menu = CreateHTML();
105
+ const commandArea = menu.querySelector('.se-controller-table-picker');
106
+ const controller_table = CreateHTML_controller_table(editor);
107
+ const controller_cell = CreateHTML_controller_cell(editor, this.cellControllerTop);
108
+ const controller_props = CreateHTML_controller_properties(editor);
109
+
110
+ editor.applyFrameRoots((e) => {
111
+ e.get('wrapper').appendChild(dom.utils.createElement('DIV', { class: RESIZE_CELL_CLASS.replace(/^\./, '') }));
112
+ e.get('wrapper').appendChild(dom.utils.createElement('DIV', { class: RESIZE_CELL_PREV_CLASS.replace(/^\./, '') }));
113
+ e.get('wrapper').appendChild(dom.utils.createElement('DIV', { class: RESIZE_ROW_CLASS.replace(/^\./, '') }));
114
+ e.get('wrapper').appendChild(dom.utils.createElement('DIV', { class: RESIZE_ROW_PREV_CLASS.replace(/^\./, '') }));
115
+ });
116
+
117
+ // members - Controller
118
+ this.controller_cell = new Controller(this, controller_cell.html, { position: this.cellControllerTop ? 'top' : 'bottom' });
119
+ this.controller_table = new Controller(this, controller_table, { position: 'top' });
120
+ if (this.cellControllerTop) {
121
+ this.controller_cell.sibling = this.controller_table.form;
122
+ this.controller_cell.siblingPosition = 'top';
115
123
  }
116
- });
124
+ // props
125
+ const propsTargetForms = [this.controller_table.form, this.controller_cell.form];
126
+ this.controller_props = new Controller(this, controller_props.html, { position: 'bottom', parents: propsTargetForms, isInsideForm: true });
127
+ this.controller_props_title = controller_props.controller_props_title;
128
+ // color picker
129
+ const colorForm = dom.utils.createElement('DIV', { class: 'se-controller se-list-layer' }, null);
130
+ this.colorPicker = new ColorPicker(this, '', {
131
+ colorList: pluginOptions.colorList || DEFAULT_COLOR_LIST,
132
+ splitNum: 5,
133
+ disableRemove: true,
134
+ hueSliderOptions: { controllerOptions: { parents: [colorForm], isOutsideForm: true } }
135
+ });
136
+
137
+ colorForm.appendChild(this.colorPicker.target);
138
+ this.controller_colorPicker = new Controller(this, colorForm, {
139
+ position: 'bottom',
140
+ parents: [this.controller_props.form].concat(propsTargetForms),
141
+ isInsideForm: true,
142
+ isWWTarget: false,
143
+ initMethod: () => {
144
+ this.colorPicker.hueSlider.close();
145
+ dom.utils.removeClass(this.controller_colorPicker.currentTarget, 'on');
146
+ }
147
+ });
148
+
149
+ this.figure = new Figure(this, null, {});
150
+
151
+ this.sliderType = '';
152
+
153
+ // members - SelectMenu [cells]
154
+ const openCellMenuFunc = _CellFormZIndex.bind(this, true);
155
+ const closeCellMenuFunc = _CellFormZIndex.bind(this, false);
156
+ // members - SelectMenu - split
157
+ const splitMenu = CreateSplitMenu(this.lang);
158
+ this.splitButton = controller_cell.splitButton;
159
+ this.selectMenu_split = new SelectMenu(this, { checkList: false, position: 'bottom-center', openMethod: openCellMenuFunc, closeMethod: closeCellMenuFunc });
160
+ this.selectMenu_split.on(this.splitButton, this.#OnSplitCells.bind(this));
161
+ this.selectMenu_split.create(splitMenu.items, splitMenu.menus);
162
+
163
+ // members - SelectMenu - column
164
+ const columnMenu = CreateColumnMenu(this.lang, this.icons);
165
+ const columnButton = controller_cell.columnButton;
166
+ this.selectMenu_column = new SelectMenu(this, { checkList: false, position: 'bottom-center', openMethod: openCellMenuFunc, closeMethod: closeCellMenuFunc });
167
+ this.selectMenu_column.on(columnButton, this.#OnColumnEdit.bind(this));
168
+ this.selectMenu_column.create(columnMenu.items, columnMenu.menus);
169
+
170
+ // members - SelectMenu - row
171
+ const rownMenu = CreateRowMenu(this.lang, this.icons);
172
+ const rowButton = controller_cell.rowButton;
173
+ this.selectMenu_row = new SelectMenu(this, { checkList: false, position: 'bottom-center', openMethod: openCellMenuFunc, closeMethod: closeCellMenuFunc });
174
+ this.selectMenu_row.on(rowButton, this.#OnRowEdit.bind(this));
175
+ this.selectMenu_row.create(rownMenu.items, rownMenu.menus);
176
+
177
+ // members - SelectMenu - properties - border style
178
+ const borderMenu = CreateBorderMenu();
179
+ const borderButton = controller_props.borderButton;
180
+ this.selectMenu_props_border = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
181
+ this.selectMenu_props_border.on(borderButton, OnPropsBorderEdit.bind(this));
182
+ this.selectMenu_props_border.create(borderMenu.items, borderMenu.menus);
183
+
184
+ // members - SelectMenu - properties - border format
185
+ const borderFormatMenu = CreateBorderFormatMenu(this.lang, this.icons, []);
186
+ const borderFormatButton = controller_props.borderFormatButton;
187
+ this.selectMenu_props_border_format = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 5 });
188
+ this.selectMenu_props_border_format.on(borderFormatButton, OnPropsBorderFormatEdit.bind(this, 'all'));
189
+ this.selectMenu_props_border_format.create(borderFormatMenu.items, borderFormatMenu.menus);
190
+
191
+ const borderFormatMenu_oneCell = CreateBorderFormatMenu(this.lang, this.icons, BORDER_FORMAT_INSIDE);
192
+ this.selectMenu_props_border_format_oneCell = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 6 });
193
+ this.selectMenu_props_border_format_oneCell.on(borderFormatButton, OnPropsBorderFormatEdit.bind(this, 'outside'));
194
+ this.selectMenu_props_border_format_oneCell.create(borderFormatMenu_oneCell.items, borderFormatMenu_oneCell.menus);
195
+
196
+ // memberts - elements..
197
+ this.maxText = this.lang.maxSize;
198
+ this.minText = this.lang.minSize;
199
+ this.propTargets = {
200
+ cell_alignment: controller_props.cell_alignment,
201
+ cell_alignment_vertical: controller_props.cell_alignment_vertical,
202
+ cell_alignment_table_text: controller_props.cell_alignment_table_text,
203
+ border_format: borderFormatButton,
204
+ border_style: controller_props.border_style,
205
+ border_color: controller_props.border_color,
206
+ border_width: controller_props.border_width,
207
+ back_color: controller_props.back_color,
208
+ font_color: controller_props.font_color,
209
+ palette_border_button: controller_props.palette_border_button,
210
+ font_bold: controller_props.font_bold,
211
+ font_underline: controller_props.font_underline,
212
+ font_italic: controller_props.font_italic,
213
+ font_strike: controller_props.font_strike
214
+ };
215
+ this._propsCache = [];
216
+ this._currentFontStyles = [];
217
+ this._propsAlignCache = '';
218
+ this._propsVerticalAlignCache = '';
219
+ this._typeCache = '';
220
+
221
+ /** @type {HTMLElement} */
222
+ this.tableHighlight = menu.querySelector('.se-table-size-highlighted');
223
+ /** @type {HTMLElement} */
224
+ this.tableUnHighlight = menu.querySelector('.se-table-size-unhighlighted');
225
+ /** @type {HTMLElement} */
226
+ this.tableDisplay = menu.querySelector('.se-table-size-display');
227
+ /** @type {HTMLButtonElement} */
228
+ this.resizeButton = controller_table.querySelector('._se_table_resize');
229
+ /** @type {HTMLSpanElement} */
230
+ this.resizeText = controller_table.querySelector('._se_table_resize > span > span');
231
+ /** @type {HTMLButtonElement} */
232
+ this.columnFixedButton = controller_table.querySelector('._se_table_fixed_column');
233
+ /** @type {HTMLButtonElement} */
234
+ this.headerButton = controller_table.querySelector('._se_table_header');
235
+ /** @type {HTMLButtonElement} */
236
+ this.captionButton = controller_table.querySelector('._se_table_caption');
237
+ /** @type {HTMLButtonElement} */
238
+ this.mergeButton = controller_cell.mergeButton;
239
+ /** @type {HTMLButtonElement} */
240
+ this.unmergeButton = controller_cell.unmergeButton;
241
+
242
+ // members - private
243
+ this._resizing = false;
244
+ this._resizeLine = null;
245
+ this._resizeLinePrev = null;
117
246
 
118
- this.figure = new Figure(this, null, {});
119
-
120
- this.sliderType = '';
121
-
122
- // members - SelectMenu - split
123
- const splitMenu = CreateSplitMenu(this.lang);
124
- this.splitButton = controller_cell.querySelector('[data-command="onsplit"]');
125
- this.selectMenu_split = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
126
- this.selectMenu_split.on(this.splitButton, OnSplitCells.bind(this));
127
- this.selectMenu_split.create(splitMenu.items, splitMenu.menus);
128
-
129
- // members - SelectMenu - column
130
- const columnMenu = CreateColumnMenu(this.lang, this.icons);
131
- const columnButton = controller_cell.querySelector('[data-command="oncolumn"]');
132
- this.selectMenu_column = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
133
- this.selectMenu_column.on(columnButton, OnColumnEdit.bind(this));
134
- this.selectMenu_column.create(columnMenu.items, columnMenu.menus);
135
-
136
- // members - SelectMenu - row
137
- const rownMenu = CreateRowMenu(this.lang, this.icons);
138
- const rowButton = controller_cell.querySelector('[data-command="onrow"]');
139
- this.selectMenu_row = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
140
- this.selectMenu_row.on(rowButton, OnRowEdit.bind(this));
141
- this.selectMenu_row.create(rownMenu.items, rownMenu.menus);
142
-
143
- // members - SelectMenu - properties - border style
144
- const borderMenu = CreateBorderMenu();
145
- const borderButton = controller_props.querySelector('[data-command="props_onborder_style"]');
146
- this.selectMenu_props_border = new SelectMenu(this, { checkList: false, position: 'bottom-center' });
147
- this.selectMenu_props_border.on(borderButton, OnPropsBorderEdit.bind(this));
148
- this.selectMenu_props_border.create(borderMenu.items, borderMenu.menus);
149
-
150
- // members - SelectMenu - properties - border format
151
- const borderFormatMenu = CreateBorderFormatMenu(this.lang, this.icons, []);
152
- const borderFormatButton = controller_props.querySelector('[data-command="props_onborder_format"]');
153
- this.selectMenu_props_border_format = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 5 });
154
- this.selectMenu_props_border_format.on(borderFormatButton, OnPropsBorderFormatEdit.bind(this, 'all'));
155
- this.selectMenu_props_border_format.create(borderFormatMenu.items, borderFormatMenu.menus);
156
-
157
- const borderFormatMenu_oneCell = CreateBorderFormatMenu(this.lang, this.icons, BORDER_FORMAT_INSIDE);
158
- this.selectMenu_props_border_format_oneCell = new SelectMenu(this, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 6 });
159
- this.selectMenu_props_border_format_oneCell.on(borderFormatButton, OnPropsBorderFormatEdit.bind(this, 'outside'));
160
- this.selectMenu_props_border_format_oneCell.create(borderFormatMenu_oneCell.items, borderFormatMenu_oneCell.menus);
161
-
162
- // memberts - elements..
163
- this.maxText = this.lang.maxSize;
164
- this.minText = this.lang.minSize;
165
- this.propTargets = {
166
- cell_alignment: controller_props.querySelector('.se-table-props-align > .__se__a_h'),
167
- cell_alignment_vertical: controller_props.querySelector('.se-table-props-align > .__se__a_v'),
168
- border_format: borderFormatButton,
169
- border_style: controller_props.querySelector('[data-command="props_onborder_style"] .se-txt'),
170
- border_color: controller_props.querySelector('.__se_border_color'),
171
- border_width: controller_props.querySelector('.__se__border_size'),
172
- back_color: controller_props.querySelector('.__se_back_color'),
173
- font_color: controller_props.querySelector('.__se_font_color'),
174
- palette_border_button: controller_props.querySelector('[data-command="props_onpalette"][data-value="border"]'),
175
- font_bold: controller_props.querySelector('[data-command="props_font_style"][data-value="bold"]'),
176
- font_underline: controller_props.querySelector('[data-command="props_font_style"][data-value="underline"]'),
177
- font_italic: controller_props.querySelector('[data-command="props_font_style"][data-value="italic"]'),
178
- font_strike: controller_props.querySelector('[data-command="props_font_style"][data-value="strike"]')
179
- };
180
- this._propsCache = [];
181
- this._currentFontStyles = [];
182
- this._propsAlignCache = '';
183
- this._propsVerticalAlignCache = '';
184
- this._typeCache = '';
185
- this.tableHighlight = menu.querySelector('.se-table-size-highlighted');
186
- this.tableUnHighlight = menu.querySelector('.se-table-size-unhighlighted');
187
- this.tableDisplay = menu.querySelector('.se-table-size-display');
188
- this.resizeButton = controller_table.querySelector('._se_table_resize');
189
- this.resizeText = controller_table.querySelector('._se_table_resize > span > span');
190
- this.columnFixedButton = controller_table.querySelector('._se_table_fixed_column');
191
- this.headerButton = controller_table.querySelector('._se_table_header');
192
- this.captionButton = controller_table.querySelector('._se_table_caption');
193
- this.mergeButton = controller_cell.querySelector('[data-command="merge"]');
194
-
195
- // members - private
196
- this._resizing = false;
197
- this._resizeLine = null;
198
- this._resizeLinePrev = null;
199
- this._figure = null;
200
- this._element = null;
201
- this._tdElement = null;
202
- this._trElement = null;
203
- this._trElements = null;
204
- this._tableXY = [];
205
- this._maxWidth = true;
206
- this._fixedColumn = false;
207
- this._physical_cellCnt = 0;
208
- this._logical_cellCnt = 0;
209
- this._rowCnt = 0;
210
- this._rowIndex = 0;
211
- this._physical_cellIndex = 0;
212
- this._logical_cellIndex = 0;
213
- this._current_colSpan = 0;
214
- this._current_rowSpan = 0;
215
-
216
- // member - multi selecte
217
- this._selectedCells = null;
218
- this._shift = false;
219
- this._fixedCell = null;
220
- this._fixedCellName = null;
221
- this._selectedCell = null;
222
- this._selectedTable = null;
223
- this._ref = null;
224
-
225
- // member - global events
226
- this._bindMultiOn = OnCellMultiSelect.bind(this);
227
- this._bindMultiOff = OffCellMultiSelect.bind(this);
228
- this._bindShiftOff = OffCellShift.bind(this);
229
- this._bindTouchOff = OffCellTouch.bind(this);
230
- this.__globalEvents = {
231
- on: null,
232
- off: null,
233
- shiftOff: null,
234
- touchOff: null,
235
- resize: null,
236
- resizeStop: null,
237
- resizeKeyDown: null
238
- };
247
+ /** @type {HTMLElement} */
248
+ this._figure = null;
249
+ /**
250
+ * @description Same value a "this._selectedTable", but it maintain prev table element
251
+ * @type {HTMLTableElement}
252
+ */
253
+ this._element = null;
254
+ /** @type {HTMLTableCellElement} */
255
+ this._tdElement = null;
256
+ /** @type {HTMLTableRowElement} */
257
+ this._trElement = null;
258
+ /** @type {HTMLTableRowElement[]|HTMLCollectionOf<HTMLTableRowElement>} */
259
+ this._trElements = null;
239
260
 
240
- // init
241
- this.menu.initDropdownTarget(Table, menu);
242
- this.eventManager.addEvent(commandArea, 'mousemove', OnMouseMoveTablePicker.bind(this));
243
- this.eventManager.addEvent(commandArea, 'click', OnClickTablePicker.bind(this));
244
- };
261
+ this._tableXY = [];
262
+ this._maxWidth = true;
263
+ this._fixedColumn = false;
264
+ this._physical_cellCnt = 0;
265
+ this._logical_cellCnt = 0;
266
+ this._cellCnt = 0;
267
+ this._rowCnt = 0;
268
+ this._rowIndex = 0;
269
+ this._physical_cellIndex = 0;
270
+ this._logical_cellIndex = 0;
271
+ this._current_colSpan = 0;
272
+ this._current_rowSpan = 0;
273
+
274
+ // member - multi selecte
275
+ /** @type {HTMLTableElement} */
276
+ this._selectedTable = null;
277
+ /** @type {HTMLTableCellElement} */
278
+ this._fixedCell = null;
279
+ /** @type {HTMLTableCellElement} */
280
+ this._selectedCell = null;
281
+ /** @type {HTMLTableCellElement[]} */
282
+ this._selectedCells = null;
283
+
284
+ this._shift = false;
285
+ this.__s = false;
286
+ this._fixedCellName = null;
287
+ this._ref = null;
288
+
289
+ // member - global events
290
+ this._bindMultiOn = this.#OnCellMultiSelect.bind(this);
291
+ this._bindMultiOff = this.#OffCellMultiSelect.bind(this);
292
+ this._bindShiftOff = this.#OffCellShift.bind(this);
293
+ this._bindTouchOff = this.#OffCellTouch.bind(this);
294
+ this.__globalEvents = {
295
+ on: null,
296
+ off: null,
297
+ shiftOff: null,
298
+ touchOff: null,
299
+ resize: null,
300
+ resizeStop: null,
301
+ resizeKeyDown: null
302
+ };
303
+
304
+ // init
305
+ this.menu.initDropdownTarget(Table, menu);
306
+ this.eventManager.addEvent(commandArea, 'mousemove', this.#OnMouseMoveTablePicker.bind(this));
307
+ this.eventManager.addEvent(commandArea, 'click', this.#OnClickTablePicker.bind(this));
308
+ }
245
309
 
246
- Table.key = 'table';
247
- Table.type = 'dropdown-free';
248
- Table.className = '';
249
- Table.component = function (node) {
250
- return domUtils.isTable(node) ? node : null;
251
- };
252
- Table.options = { isInputComponent: true };
253
- Table.prototype = {
254
310
  /**
255
- * @override core
311
+ * @editorMethod Editor.core
312
+ * @description Executes the main execution method of the plugin.
313
+ * - Called when an item in the "dropdown" menu is clicked.
256
314
  */
257
315
  action() {
258
- const oTable = domUtils.createElement('TABLE');
316
+ const oTable = dom.utils.createElement('TABLE');
259
317
  const x = this._tableXY[0];
260
318
  const y = this._tableXY[1];
261
319
 
262
- const body = `<tbody>${`<tr>${CreateCells('td', x, false)}</tr>`.repeat(y)}</tbody>`;
320
+ const body = `<tbody>${`<tr>${CreateCellsString('td', x)}</tr>`.repeat(y)}</tbody>`;
263
321
  const colGroup = `<colgroup>${`<col style="width: ${numbers.get(100 / x, CELL_DECIMAL_END)}%;">`.repeat(x)}</colgroup>`;
264
322
  oTable.innerHTML = colGroup + body;
265
323
 
266
- const figure = domUtils.createElement('FIGURE', { class: 'se-flex-component se-input-component' });
324
+ // scroll
325
+ let scrollTypeClass = '';
326
+ if (this.figureScroll) {
327
+ scrollTypeClass = ` se-scroll-figure-${this.figureScroll}`;
328
+ }
329
+
330
+ const figure = dom.utils.createElement('FIGURE', { class: 'se-flex-component se-input-component' + scrollTypeClass });
267
331
  figure.appendChild(oTable);
268
332
 
269
- if (this.component.insert(figure, false, false)) {
333
+ if (this.component.insert(figure, { skipCharCount: false, skipSelection: false, skipHistory: false })) {
270
334
  this._resetTablePicker();
271
335
  const target = oTable.querySelector('td div');
272
336
  this.selection.setRange(target, 0, target, 0);
273
337
  }
274
- },
338
+ }
339
+
340
+ /**
341
+ * @editorMethod Editor.component
342
+ * @description Executes the method that is called when a component of a plugin is selected.
343
+ * @param {HTMLElement} target Target component element
344
+ */
345
+ select(target) {
346
+ this._figureOpen(target);
347
+ if (!this._figure) this.setTableInfo(target);
348
+
349
+ const targetWidth = this._figure?.style.width || '100%';
350
+ this._maxWidth = targetWidth === '100%';
351
+ this._fixedColumn = dom.utils.hasClass(target, 'se-table-layout-fixed') || target.style.tableLayout === 'fixed';
352
+ this._setTableStyle(this._maxWidth ? 'width|column' : 'width', true);
353
+
354
+ if (_DragHandle.get('__overInfo') === ON_OVER_COMPONENT) return;
355
+
356
+ if (!this._tdElement) return;
357
+ this.setCellInfo(this._tdElement, true);
358
+
359
+ // controller open
360
+ const btnDisabled = this._selectedCells?.length > 1;
361
+ const figureEl = dom.query.getParentElement(target, dom.check.isFigure);
362
+ this.controller_table.open(figureEl, null, { isWWTarget: false, initMethod: null, addOffset: null, disabled: btnDisabled });
363
+
364
+ if (!this._fixedCell) return;
365
+
366
+ this._setUnMergeButton();
367
+ this.controller_cell.open(this._tdElement, this.cellControllerTop ? figureEl : null, { isWWTarget: false, initMethod: null, addOffset: null, disabled: btnDisabled });
368
+ }
369
+
370
+ /**
371
+ * @editorMethod Editor.component
372
+ * @description Executes the method that is called when a component copy is requested.
373
+ * @param {__se__PluginCopyComponentParams} params
374
+ * @returns {boolean|void}
375
+ */
376
+ onCopyComponent({ event, cloneContainer }) {
377
+ /** @type {NodeListOf<HTMLTableCellElement>} */
378
+ const selectedCells = cloneContainer.querySelectorAll('.se-selected-table-cell');
379
+ dom.utils.removeClass(selectedCells, 'se-selected-table-cell|se-selected-cell-focus');
380
+
381
+ if (selectedCells.length > 0) {
382
+ SetClipboardSelectedTableCells(event, cloneContainer, selectedCells);
383
+ this.editor.ui.showToast(this.lang.message_copy_success, 550);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * @editorMethod Editor.EventManager
389
+ * @description Executes the event function of "copy".
390
+ * @param {__se__PluginPasteParams} params
391
+ * @returns {boolean|void}
392
+ */
393
+ onPaste({ event, doc }) {
394
+ /** @type {HTMLTableCellElement} */
395
+ const targetCell = dom.query.getParentElement(dom.query.getEventTarget(event), dom.check.isTableCell);
396
+ if (!targetCell) return;
397
+
398
+ const domParserBody = doc.body;
399
+ if (domParserBody.childElementCount !== 1) return;
400
+
401
+ const componentInfo = this.component.get(domParserBody.firstElementChild);
402
+ if (componentInfo.pluginName !== Table.key) return;
403
+
404
+ const copyTable = /** @type {HTMLTableElement} */ (componentInfo.target);
405
+ this.pasteTableCellMatrix(copyTable, targetCell);
406
+
407
+ return true;
408
+ }
275
409
 
276
410
  /**
277
- * @override core
411
+ * @editorMethod Editor.core
412
+ * @description This method is used to validate and preserve the format of the component within the editor.
413
+ * - It ensures that the structure and attributes of the element are maintained and secure.
414
+ * - The method checks if the element is already wrapped in a valid container and updates its attributes if necessary.
415
+ * - If the element isn't properly contained, a new container is created to retain the format.
416
+ * @returns {{query: string, method: (element: HTMLTableElement) => void}} The format retention object containing the query and method to process the element.
417
+ * - query: The selector query to identify the relevant elements (in this case, 'audio').
418
+ * - method:The function to execute on the element to validate and preserve its format.
419
+ * - The function takes the element as an argument, checks if it is contained correctly, and applies necessary adjustments.
278
420
  */
279
421
  retainFormat() {
280
422
  return {
281
423
  query: 'table',
282
424
  method: (element) => {
283
425
  const ColgroupEl = element.querySelector('colgroup');
284
- let FigureEl = domUtils.isFigure(element.parentNode) ? element.parentNode : null;
426
+ let FigureEl = dom.check.isFigure(element.parentNode) ? element.parentNode : null;
285
427
  if (ColgroupEl && FigureEl) return;
286
428
 
287
429
  // create colgroup
288
430
  if (!ColgroupEl) {
289
431
  const maxCount = GetMaxColumns(element);
290
- const colGroup = domUtils.createElement('colgroup', null, `<col style="width: ${numbers.get(100 / maxCount, CELL_DECIMAL_END)}%;">`.repeat(maxCount));
432
+ const colGroup = dom.utils.createElement('colgroup', null, `<col style="width: ${numbers.get(100 / maxCount, CELL_DECIMAL_END)}%;">`.repeat(maxCount));
291
433
  element.insertBefore(colGroup, element.firstElementChild);
292
434
  }
293
435
 
294
436
  // figure
295
437
  if (!FigureEl) {
296
- FigureEl = domUtils.createElement('FIGURE', { class: 'se-flex-component se-input-component' });
438
+ FigureEl = dom.utils.createElement('FIGURE', { class: 'se-flex-component se-input-component' });
297
439
  element.parentNode.insertBefore(FigureEl, element);
298
440
  FigureEl.appendChild(element);
299
441
  } else {
300
- domUtils.addClass(FigureEl, 'se-flex-component|se-input-component');
442
+ dom.utils.addClass(FigureEl, 'se-flex-component|se-input-component');
301
443
  }
302
444
 
303
445
  // scroll
304
446
  if (!this.figureScroll) {
305
- domUtils.removeClass(FigureEl, this.figureScrollList.join('|'));
447
+ dom.utils.removeClass(FigureEl, this.figureScrollList.join('|'));
306
448
  } else {
307
449
  const scrollTypeClass = `se-scroll-figure-${this.figureScroll}`;
308
- domUtils.addClass(FigureEl, scrollTypeClass);
309
- domUtils.removeClass(FigureEl, this.figureScrollList.filter((v) => v !== scrollTypeClass).join('|'));
450
+ dom.utils.addClass(FigureEl, scrollTypeClass);
451
+ dom.utils.removeClass(FigureEl, this.figureScrollList.filter((v) => v !== scrollTypeClass).join('|'));
310
452
  }
311
453
  }
312
454
  };
313
- },
455
+ }
314
456
 
315
457
  /**
316
- * @override core
317
- * @param {"rtl"|"ltr"} dir Direction
458
+ * @editorMethod Editor.core
459
+ * @description Executes the method called when the rtl, ltr mode changes. ("editor.setDir")
460
+ * @param {string} dir Direction ("rtl" or "ltr")
318
461
  */
319
462
  setDir(dir) {
320
463
  this.tableHighlight.style.left = dir === 'rtl' ? 10 * 18 - 13 + 'px' : '';
321
464
  this._resetTablePicker();
322
- this._resetPropsAlign(dir === 'rtl');
323
- },
465
+ this._resetPropsAlign();
466
+ }
324
467
 
468
+ /**
469
+ * @editorMethod Editor.EventManager
470
+ * @description Executes the event function of "mousemove".
471
+ * @param {__se__PluginMouseEventInfo} params
472
+ */
325
473
  onMouseMove({ event }) {
326
474
  if (this._resizing) return;
327
- const target = domUtils.getParentElement(event.target, IsResizeEls);
475
+
476
+ const eventTarget = dom.query.getEventTarget(event);
477
+ const target = dom.query.getParentElement(eventTarget, IsResizeEls);
328
478
  if (!target || this._fixedCell) {
329
479
  this.__hideResizeLine();
330
480
  return;
@@ -336,7 +486,7 @@ Table.prototype = {
336
486
  this.__removeGlobalEvents();
337
487
  if (this._resizeLine?.style.display === 'block') this._resizeLine.style.display = 'none';
338
488
  this._resizeLine = this.editor.frameContext.get('wrapper').querySelector(RESIZE_CELL_CLASS);
339
- this._setResizeLinePosition(domUtils.getParentElement(target, domUtils.isTable), target, this._resizeLine, cellEdge.isLeft);
489
+ this._setResizeLinePosition(dom.query.getParentElement(target, dom.check.isTable), target, this._resizeLine, cellEdge.isLeft);
340
490
  this._resizeLine.style.display = 'block';
341
491
  return;
342
492
  }
@@ -344,35 +494,45 @@ Table.prototype = {
344
494
  const rowEdge = CheckRowEdge(event, target);
345
495
  if (rowEdge.is) {
346
496
  this.__removeGlobalEvents();
347
- this._element = domUtils.getParentElement(target, domUtils.isTable);
497
+ this._element = dom.query.getParentElement(target, dom.check.isTable);
348
498
  this._element.style.cursor = 'ns-resize';
349
499
  if (this._resizeLine?.style.display === 'block') this._resizeLine.style.display = 'none';
350
500
  this._resizeLine = this.editor.frameContext.get('wrapper').querySelector(RESIZE_ROW_CLASS);
351
- this._setResizeRowPosition(domUtils.getParentElement(target, domUtils.isTable), target, this._resizeLine);
501
+ this._setResizeRowPosition(dom.query.getParentElement(target, dom.check.isTable), target, this._resizeLine);
352
502
  this._resizeLine.style.display = 'block';
353
503
  return;
354
504
  }
355
505
 
356
506
  if (this._element) this._element.style.cursor = '';
357
507
  this.__hideResizeLine();
358
- },
508
+ }
359
509
 
510
+ /**
511
+ * @editorMethod Editor.EventManager
512
+ * @description Executes the event function of "scroll".
513
+ */
360
514
  onScroll() {
361
515
  if (this._resizeLine?.style.display !== 'block') return;
362
516
  // delete resize line position
363
517
  if (this._element) this._element.style.cursor = '';
364
518
  this._resizeLine.style.display = 'none';
365
- },
519
+ }
366
520
 
367
521
  /**
368
- * @override core
369
- * @param {any} event Event object
522
+ * @editorMethod Editor.EventManager
523
+ * @description Executes the event function of "mousedown".
524
+ * @param {__se__PluginMouseEventInfo} params
370
525
  */
371
526
  onMouseDown({ event }) {
372
- this._ref = null;
373
- const target = domUtils.getParentElement(event.target, IsResizeEls);
527
+ this._ref = this._selectedCell = null;
528
+ const eventTarget = dom.query.getEventTarget(event);
529
+ const target = /** @type {HTMLTableCellElement} */ (dom.query.getParentElement(eventTarget, IsResizeEls));
374
530
  if (!target) return;
375
531
 
532
+ if (!this.cellControllerTop) {
533
+ this.controller_cell.hide();
534
+ }
535
+
376
536
  const cellEdge = CheckCellEdge(event, target);
377
537
  if (cellEdge.is) {
378
538
  try {
@@ -381,7 +541,7 @@ Table.prototype = {
381
541
  const colIndex = this._logical_cellIndex - (cellEdge.isLeft ? 1 : 0);
382
542
 
383
543
  // ready
384
- this.editor.enableBackWrapper('ew-resize');
544
+ this.ui.enableBackWrapper('ew-resize');
385
545
  if (!this._resizeLine) this._resizeLine = this.editor.frameContext.get('wrapper').querySelector(RESIZE_CELL_CLASS);
386
546
  this._resizeLinePrev = this.editor.frameContext.get('wrapper').querySelector(RESIZE_CELL_PREV_CLASS);
387
547
 
@@ -393,9 +553,12 @@ Table.prototype = {
393
553
 
394
554
  const col = this._element.querySelector('colgroup').querySelectorAll('col')[colIndex < 0 ? 0 : colIndex];
395
555
  this._startCellResizing(col, cellEdge.startX, numbers.get(_w.getComputedStyle(col).width, CELL_DECIMAL_END), cellEdge.isLeft);
556
+ this._toggleEditor(false);
396
557
  } catch (err) {
397
558
  console.warn('[SUNEDITOR.plugins.table.error]', err);
398
559
  this.__removeGlobalEvents();
560
+ } finally {
561
+ this._fixedCell = this._selectedCell = null;
399
562
  }
400
563
 
401
564
  return;
@@ -404,11 +567,12 @@ Table.prototype = {
404
567
  const rowEdge = CheckRowEdge(event, target);
405
568
  if (rowEdge.is) {
406
569
  try {
407
- let row = domUtils.getParentElement(target, domUtils.isTableRow);
570
+ /** @type {HTMLTableRowElement} */
571
+ let row = dom.query.getParentElement(target, dom.check.isTableRow);
408
572
  let rowSpan = target.rowSpan;
409
573
  if (rowSpan > 1) {
410
- while (domUtils.isTableRow(row) && rowSpan > 1) {
411
- row = row.nextElementSibling;
574
+ while (dom.check.isTableRow(row) && rowSpan > 1) {
575
+ row = /** @type {HTMLTableRowElement} */ (row.nextElementSibling);
412
576
  --rowSpan;
413
577
  }
414
578
  }
@@ -417,67 +581,87 @@ Table.prototype = {
417
581
  this.setRowInfo(row);
418
582
 
419
583
  // ready
420
- this.editor.enableBackWrapper('ns-resize');
584
+ this.ui.enableBackWrapper('ns-resize');
421
585
  if (!this._resizeLine) this._resizeLine = this.editor.frameContext.get('wrapper').querySelector(RESIZE_ROW_CLASS);
422
586
  this._resizeLinePrev = this.editor.frameContext.get('wrapper').querySelector(RESIZE_ROW_PREV_CLASS);
423
587
 
424
588
  this._startRowResizing(row, rowEdge.startY, numbers.get(_w.getComputedStyle(row).height, CELL_DECIMAL_END));
589
+ this._toggleEditor(false);
425
590
  } catch (err) {
426
591
  console.warn('[SUNEDITOR.plugins.table.error]', err);
427
592
  this.__removeGlobalEvents();
593
+ } finally {
594
+ this._fixedCell = this._selectedCell = null;
428
595
  }
429
596
 
430
597
  return;
431
598
  }
432
599
 
433
- if (!(target !== this._fixedCell && !this._shift)) return;
600
+ if (this._shift && target !== this._fixedCell) return;
434
601
 
435
602
  this._deleteStyleSelectedCells();
436
603
  if (/^TR$/i.test(target.nodeName)) return;
437
604
 
438
- this.selectCells(target, false);
439
- },
605
+ this.#StyleSelectCells(target, false);
606
+ }
440
607
 
441
608
  /**
442
- * @override core
609
+ * @editorMethod Editor.EventManager
610
+ * @description Executes the event function of "mouseup".
443
611
  */
444
612
  onMouseUp() {
445
613
  this._shift = false;
446
- },
614
+ if (!this.cellControllerTop) {
615
+ this.controller_cell.resetPosition(this._fixedCell);
616
+ }
617
+ }
447
618
 
448
619
  /**
449
- * @override core
620
+ * @editorMethod Editor.EventManager
621
+ * @description Executes the event function of "mouseleave".
450
622
  */
451
623
  onMouseLeave() {
452
624
  this.__hideResizeLine();
453
- },
625
+ }
454
626
 
455
627
  /**
456
- * @override core
457
- * @param {any} event Event object
458
- * @param {any} range range object
459
- * @param {Element} line Current line element
628
+ * @editorMethod Editor.EventManager
629
+ * @description Executes the event function of "keydown".
630
+ * @param {__se__PluginKeyEventInfo} params
460
631
  */
461
632
  onKeyDown({ event, range, line }) {
462
633
  this._ref = null;
463
- if (this.editor.selectMenuOn || this._resizing) return;
464
634
 
465
- const keyCode = event.keyCode;
635
+ const keyCode = event.code;
636
+ const isTab = keyCodeMap.isTab(keyCode);
637
+ if (this.editor.selectMenuOn || this._resizing || (!isTab && this.__s) || keyCodeMap.isCtrl(event)) return;
638
+
639
+ if (!this.cellControllerTop) {
640
+ this.controller_cell.hide();
641
+ }
642
+
643
+ this.__s = keyCodeMap.isShift(event);
644
+
466
645
  // table tabkey
467
- if (keyCode === 9) {
468
- const tableCell = domUtils.getParentElement(line, domUtils.isTableCell);
469
- if (tableCell && range.collapsed && domUtils.isEdgePoint(range.startContainer, range.startOffset)) {
646
+ if (isTab) {
647
+ this._deleteStyleSelectedCells();
648
+ const tableCell = dom.query.getParentElement(line, dom.check.isTableCell);
649
+ if (tableCell && range.collapsed && dom.check.isEdgePoint(range.startContainer, range.startOffset)) {
470
650
  this._closeController();
471
651
 
472
- const shift = event.shiftKey;
473
- const table = domUtils.getParentElement(tableCell, 'table');
474
- const cells = domUtils.getListChildren(table, domUtils.isTableCell);
475
- const idx = shift ? domUtils.prevIndex(cells, tableCell) : domUtils.nextIndex(cells, tableCell);
652
+ const shift = this.__s;
653
+ this._shift = this.__s = false;
654
+
655
+ /** @type {HTMLTableElement} */
656
+ const table = dom.query.getParentElement(tableCell, 'table');
657
+ /** @type {HTMLTableCellElement[]} */
658
+ const cells = dom.query.getListChildren(table, dom.check.isTableCell);
659
+ const idx = shift ? dom.utils.prevIndex(cells, tableCell) : dom.utils.nextIndex(cells, tableCell);
476
660
 
477
661
  if (idx === cells.length && !shift) {
478
- if (!domUtils.getParentElement(tableCell, 'thead')) {
662
+ if (!dom.query.getParentElement(tableCell, 'thead')) {
479
663
  const rows = table.rows;
480
- const newRow = this.insertBodyRow(table, rows.length, rows[rows.length - 1].cells.length);
664
+ const newRow = this.insertBodyRow(table, rows.length, this._cellCnt);
481
665
  const firstTd = newRow.querySelector('td div');
482
666
  this.selection.setRange(firstTd, 0, firstTd, 0);
483
667
  }
@@ -488,13 +672,13 @@ Table.prototype = {
488
672
  return false;
489
673
  }
490
674
 
491
- if (idx === -1 && shift) return;
675
+ if (idx === -1 && shift) return false;
492
676
 
493
- let moveCell = cells[idx];
677
+ const moveCell = cells[idx];
494
678
  if (!moveCell) return;
495
679
 
496
- moveCell = moveCell.firstElementChild || moveCell;
497
- this.selection.setRange(moveCell, 0, moveCell, 0);
680
+ const rangeCell = moveCell.firstElementChild || moveCell;
681
+ this.selection.setRange(rangeCell, 0, rangeCell, 0);
498
682
 
499
683
  event.preventDefault();
500
684
  event.stopPropagation();
@@ -504,9 +688,9 @@ Table.prototype = {
504
688
  }
505
689
 
506
690
  let cell = null;
507
- if (!event.shiftKey || keyCode !== 16) {
508
- cell = domUtils.getParentElement(line, domUtils.isTableCell);
509
- if (!domUtils.hasClass(cell, 'se-selected-cell-focus')) return;
691
+ if (!keyCodeMap.isShift(event)) {
692
+ cell = dom.query.getParentElement(line, dom.check.isTableCell);
693
+ if (!dom.utils.hasClass(cell, 'se-selected-cell-focus')) return;
510
694
 
511
695
  this._deleteStyleSelectedCells();
512
696
  this._toggleEditor(true);
@@ -518,43 +702,46 @@ Table.prototype = {
518
702
 
519
703
  if (this._shift || this._ref) return;
520
704
 
521
- cell = cell || domUtils.getParentElement(line, domUtils.isTableCell);
705
+ cell = /** @type {HTMLTableCellElement} */ (cell || dom.query.getParentElement(line, dom.check.isTableCell));
522
706
  if (cell) {
707
+ this.__s = false;
523
708
  this._fixedCell = cell;
524
709
  this._closeController();
525
- this.selectCells(cell, event.shiftKey);
710
+ this.#StyleSelectCells(cell, event.shiftKey);
526
711
  return false;
527
712
  }
528
- },
713
+ }
529
714
 
530
715
  /**
531
- * @override core
532
- * @param {any} event Event object
533
- * @param {any} range range object
534
- * @param {Element} line Current line element
716
+ * @editorMethod Editor.EventManager
717
+ * @description Executes the event function of "keyup".
718
+ * @param {__se__PluginKeyEventInfo} params
535
719
  */
536
720
  onKeyUp({ line }) {
537
- if (this._shift && domUtils.getParentElement(line, domUtils.isTableCell) === this._fixedCell) {
721
+ this.__s = false;
722
+ if (this._shift && dom.query.getParentElement(line, dom.check.isTableCell) === this._fixedCell) {
538
723
  this._deleteStyleSelectedCells();
539
724
  this._toggleEditor(true);
540
725
  this.__removeGlobalEvents();
541
726
  }
542
727
  this._shift = false;
543
- },
728
+ }
544
729
 
545
730
  /**
546
- * @override ColorPicker
731
+ * @editorMethod Modules.ColorPicker
732
+ * @description Executes the method called when a button of "ColorPicker" module is clicked.
733
+ * @param {string} color - Color code (hex)
547
734
  */
548
735
  colorPickerAction(color) {
549
736
  const target = this.propTargets[`${this.sliderType}_color`];
550
737
  target.style.borderColor = target.value = color;
551
738
  this.controller_colorPicker.close();
552
- },
739
+ }
553
740
 
554
741
  /**
555
- * @override controller
556
- * @param {Element} target Target button element
557
- * @returns
742
+ * @editorMethod Modules.Controller
743
+ * @description Executes the method that is called when a button is clicked in the "controller".
744
+ * @param {HTMLButtonElement} target Target button element
558
745
  */
559
746
  controllerAction(target) {
560
747
  const command = target.getAttribute('data-command');
@@ -614,7 +801,7 @@ Table.prototype = {
614
801
  this._onColorPalette(target, value, value === 'border' ? border_color : value === 'back' ? back_color : font_color);
615
802
  break;
616
803
  case 'props_font_style':
617
- domUtils.toggleClass(this.propTargets[`font_${value}`], 'on');
804
+ dom.utils.toggleClass(this.propTargets[`font_${value}`], 'on');
618
805
  break;
619
806
  case 'props_submit':
620
807
  this._submitProps(target);
@@ -627,7 +814,7 @@ Table.prototype = {
627
814
  // alignment
628
815
  this._setAlignProps(this.propTargets.cell_alignment, this._propsAlignCache, true);
629
816
  this._setAlignProps(this.propTargets.cell_alignment_vertical, this._propsVerticalAlignCache, true);
630
- if (domUtils.isTable(propsCache[0][0]) && this._figure) {
817
+ if (dom.check.isTable(propsCache[0][0]) && this._figure) {
631
818
  this._figure.style.float = this._propsAlignCache;
632
819
  }
633
820
  break;
@@ -642,24 +829,31 @@ Table.prototype = {
642
829
  this._setAlignProps(this.propTargets.cell_alignment_vertical, target.getAttribute('data-value'), false);
643
830
  break;
644
831
  case 'merge':
645
- this.mergeCells();
832
+ this.mergeCells(this._selectedCells);
833
+ break;
834
+ case 'unmerge':
835
+ this.unmergeCells(this._selectedCells);
646
836
  break;
647
837
  case 'resize':
648
838
  this._maxWidth = !this._maxWidth;
649
- this.setTableStyle('width', false);
839
+ this._setTableStyle('width', false);
650
840
  this._historyPush();
651
- this.component.select(this._element, Table.key, true);
841
+ this.component.select(this._element, Table.key, { isInput: true });
652
842
  break;
653
843
  case 'layout':
654
844
  this._fixedColumn = !this._fixedColumn;
655
- this.setTableStyle('column', false);
845
+ this._setTableStyle('column', false);
656
846
  this._historyPush();
657
- this.component.select(this._element, Table.key, true);
847
+ this.component.select(this._element, Table.key, { isInput: true });
848
+ break;
849
+ case 'copy':
850
+ this.component.copy(this._figure);
658
851
  break;
659
852
  case 'remove': {
660
853
  const emptyDiv = this._figure?.parentNode;
661
- domUtils.removeItem(this._figure);
662
- this._closeController();
854
+ dom.utils.removeItem(this._figure);
855
+
856
+ this._closeTableSelectInfo();
663
857
 
664
858
  if (emptyDiv !== this.editor.frameContext.get('wysiwyg'))
665
859
  this.nodeTransform.removeAllParents(
@@ -680,12 +874,13 @@ Table.prototype = {
680
874
  }
681
875
 
682
876
  if (!/^(remove|props_|on|open|merge)/.test(command)) {
683
- this.setCellControllerPosition(this._tdElement, this._shift);
877
+ this._setCellControllerPosition(this._tdElement, this._shift);
684
878
  }
685
- },
879
+ }
686
880
 
687
881
  /**
688
- * @override controller
882
+ * @editorMethod Modules.Controller
883
+ * @description Executes the method called when the "controller" is closed.
689
884
  */
690
885
  close() {
691
886
  this.__removeGlobalEvents();
@@ -694,7 +889,6 @@ Table.prototype = {
694
889
 
695
890
  this._figure = null;
696
891
  this._element = null;
697
- // this._tdElement = null;
698
892
  this._trElement = null;
699
893
  this._trElements = null;
700
894
  this._tableXY = [];
@@ -719,56 +913,63 @@ Table.prototype = {
719
913
  this._fixedCellName = null;
720
914
 
721
915
  const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike } = this.propTargets;
722
- domUtils.removeClass([border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike], 'on');
723
- },
724
-
725
- selectCells(tdElement, shift) {
726
- if (!this._shift && !this._ref) this.__removeGlobalEvents();
727
-
728
- this._shift = shift;
729
- this._fixedCell = tdElement;
730
- this._fixedCellName = tdElement.nodeName;
731
- this._selectedTable = domUtils.getParentElement(tdElement, 'TABLE');
916
+ dom.utils.removeClass([border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike], 'on');
917
+ }
732
918
 
733
- this._deleteStyleSelectedCells();
734
- domUtils.addClass(tdElement, 'se-selected-cell-focus');
919
+ /**
920
+ * @description Selects a group of table cells and sets internal state related to multi-cell selection.
921
+ * @param {HTMLTableCellElement[]} cells - An array of table cell elements to be selected.
922
+ */
923
+ selectCells(cells) {
924
+ const firstCell = cells[0];
925
+ const lastCell = dom.query.findVisualLastCell(cells);
735
926
 
736
- if (!shift) {
737
- this.__globalEvents.on = this.eventManager.addGlobalEvent('mousemove', this._bindMultiOn, false);
738
- } else {
739
- this.__globalEvents.shiftOff = this.eventManager.addGlobalEvent('keyup', this._bindShiftOff, false);
740
- this.__globalEvents.on = this.eventManager.addGlobalEvent('mousedown', this._bindMultiOn, false);
741
- }
927
+ this._selectedCells = cells;
928
+ this._fixedCell = firstCell;
929
+ this._selectedCell = lastCell;
930
+ this._fixedCellName = firstCell.nodeName;
931
+ this._selectedTable = dom.query.getParentElement(firstCell, 'TABLE');
742
932
 
743
- this.__globalEvents.off = this.eventManager.addGlobalEvent('mouseup', this._bindMultiOff, false);
744
- this.__globalEvents.touchOff = this.eventManager.addGlobalEvent('touchmove', this._bindTouchOff, false);
745
- },
933
+ this._setMultiCells(firstCell, lastCell);
934
+ }
746
935
 
747
- seTableInfo(element) {
748
- const table = (this._element = this._selectedTable || domUtils.getParentElement(element, 'TABLE'));
749
- this._figure = domUtils.getParentElement(table, domUtils.isFigure) || table;
750
- return table;
751
- },
936
+ /**
937
+ * @description Sets the table and figure elements based on the provided cell element, and stores references to them for later use.
938
+ * @param {Node} element The target table cell (`<td>`) element from which the table info will be extracted.
939
+ * @returns {HTMLTableElement} The `<table>` element that is the parent of the provided `element`.
940
+ */
941
+ setTableInfo(element) {
942
+ const table = (this._element = this._selectedTable = dom.query.getParentElement(element, 'TABLE'));
943
+ this._figure = dom.query.getParentElement(table, dom.check.isFigure) || table;
944
+ return /** @type {HTMLTableElement} */ (table);
945
+ }
752
946
 
947
+ /**
948
+ * @description Sets various table-related information based on the provided table cell element (`<td>`). This includes updating cell, row, and table attributes, handling spanning cells, and adjusting the UI for elements like headers and captions.
949
+ * @param {HTMLTableCellElement} tdElement The target table cell (`<td>`) element from which table information will be extracted.
950
+ * @param {boolean} reset A flag indicating whether to reset the cell information. If `true`, the cell information will be reset and recalculated.
951
+ */
753
952
  setCellInfo(tdElement, reset) {
754
- const table = this.seTableInfo(tdElement);
755
- this._trElement = tdElement.parentNode;
953
+ const table = this.setTableInfo(tdElement);
954
+ if (!table) return;
955
+ this._fixedCell = tdElement;
956
+ this._trElement = /** @type {HTMLTableRowElement} */ (tdElement.parentNode);
756
957
 
757
958
  // hedaer
758
- if (table.querySelector('thead')) domUtils.addClass(this.headerButton, 'active');
759
- else domUtils.removeClass(this.headerButton, 'active');
959
+ if (table.querySelector('thead')) dom.utils.addClass(this.headerButton, 'active');
960
+ else dom.utils.removeClass(this.headerButton, 'active');
760
961
 
761
962
  // caption
762
- if (table.querySelector('caption')) domUtils.addClass(this.captionButton, 'active');
763
- else domUtils.removeClass(this.captionButton, 'active');
963
+ if (table.querySelector('caption')) dom.utils.addClass(this.captionButton, 'active');
964
+ else dom.utils.removeClass(this.captionButton, 'active');
764
965
 
765
966
  if (reset || this._physical_cellCnt === 0) {
766
967
  if (this._tdElement !== tdElement) {
767
968
  this._tdElement = tdElement;
768
- this._trElement = tdElement.parentNode;
969
+ this._trElement = /** @type {HTMLTableRowElement} */ (tdElement.parentNode);
769
970
  }
770
971
 
771
- if (!this._selectedCells || this._selectedCells.length === 0) this._selectedCells = [tdElement];
972
+ if (!this._selectedCells?.length) this._selectedCells = [tdElement];
772
973
 
773
974
  const rows = (this._trElements = table.rows);
774
975
  const cellIndex = tdElement.cellIndex;
@@ -784,7 +985,7 @@ Table.prototype = {
784
985
 
785
986
  // cell cnt, physical cell index
786
987
  this._physical_cellCnt = this._trElement.cells.length;
787
- this._logical_cellCnt = cellCnt;
988
+ this._logical_cellCnt = this._cellCnt = cellCnt;
788
989
  this._physical_cellIndex = cellIndex;
789
990
 
790
991
  // span
@@ -854,30 +1055,39 @@ Table.prototype = {
854
1055
  rowSpanArr = null;
855
1056
  spanIndex = null;
856
1057
  }
857
- },
1058
+ }
858
1059
 
1060
+ /**
1061
+ * @description Sets row-related information based on the provided table row element (`<tr>`). This includes updating the row count and the index of the selected row.
1062
+ * @param {HTMLTableRowElement} trElement The target table row (`<tr>`) element from which row information will be extracted.
1063
+ */
859
1064
  setRowInfo(trElement) {
860
- const table = this.seTableInfo(trElement);
1065
+ const table = this.setTableInfo(trElement);
861
1066
  const rows = (this._trElements = table.rows);
862
1067
  this._rowCnt = rows.length;
863
1068
  this._rowIndex = trElement.rowIndex;
864
- },
1069
+ }
865
1070
 
1071
+ /**
1072
+ * @description Edits the table by adding, removing, or modifying rows and cells, based on the provided options. Supports both single and multi-cell/row editing.
1073
+ * @param {"row"|"cell"} type The type of element to edit ('row' or 'cell').
1074
+ * @param {?"up"|"down"|"left"|"right"} option The action to perform: 'up', 'down', 'left', 'right', or `null` for removing.
1075
+ */
866
1076
  editTable(type, option) {
867
1077
  const table = this._element;
868
1078
  const isRow = type === 'row';
869
1079
 
870
1080
  if (isRow) {
871
- const tableAttr = this._trElement.parentNode;
1081
+ const tableAttr = this._trElement.parentElement;
872
1082
  if (/^THEAD$/i.test(tableAttr.nodeName)) {
873
1083
  if (option === 'up') {
874
1084
  return;
875
1085
  } else if (!tableAttr.nextElementSibling || !/^TBODY$/i.test(tableAttr.nextElementSibling.nodeName)) {
876
1086
  if (!option) {
877
- domUtils.removeItem(this._figure);
878
- this._closeController();
1087
+ dom.utils.removeItem(this._figure);
1088
+ this._closeTableSelectInfo();
879
1089
  } else {
880
- table.innerHTML += '<tbody><tr>' + CreateCells('td', this._logical_cellCnt, false) + '</tr></tbody>';
1090
+ table.innerHTML += '<tbody><tr>' + CreateCellsString('td', this._logical_cellCnt) + '</tr></tbody>';
881
1091
  }
882
1092
  return;
883
1093
  }
@@ -910,7 +1120,7 @@ Table.prototype = {
910
1120
  } else {
911
1121
  // edit row
912
1122
  this.setCellInfo(option === 'up' ? selectedCells[0] : selectedCells[selectedCells.length - 1], true);
913
- this.editRow(option, positionCell);
1123
+ this.editRow(option, null, positionCell);
914
1124
  }
915
1125
  } else {
916
1126
  // multi - cell
@@ -944,7 +1154,7 @@ Table.prototype = {
944
1154
  }
945
1155
 
946
1156
  this.setCellInfo(option === 'left' ? selectedCells[0] : rightCell || selectedCells[0], true);
947
- this.editCell(option, positionCell);
1157
+ this.editCell(option, null, positionCell);
948
1158
  }
949
1159
  }
950
1160
 
@@ -959,16 +1169,28 @@ Table.prototype = {
959
1169
  const children = table.children;
960
1170
  for (let i = 0; i < children.length; i++) {
961
1171
  if (children[i].children.length === 0) {
962
- domUtils.removeItem(children[i]);
1172
+ dom.utils.removeItem(children[i]);
963
1173
  i--;
964
1174
  }
965
1175
  }
966
1176
 
967
- if (table.children.length === 0) domUtils.removeItem(table);
1177
+ if (table.children.length === 0) dom.utils.removeItem(table);
968
1178
  }
969
- },
1179
+ }
1180
+
1181
+ /**
1182
+ * @description Edits a table row, either adding, removing, the row
1183
+ * @param {?string} option The action to perform on the row ("up"|"down"|null)
1184
+ * - null: to remove the row
1185
+ * - 'up': to insert the row up
1186
+ * - 'down': to insert the row down, or null to remove.
1187
+ * @param {?HTMLTableCellElement=} targetCell Target cell, (default: current selected cell)
1188
+ * @param {?HTMLTableCellElement=} [positionResetElement] The element to reset the position of (optional). This can be the cell that triggered the row edit.
1189
+ */
1190
+ editRow(option, targetCell, positionResetElement) {
1191
+ this._deleteStyleSelectedCells();
1192
+ if (targetCell) this.setCellInfo(targetCell, true);
970
1193
 
971
- editRow(option, positionResetElement) {
972
1194
  const remove = !option;
973
1195
  const up = option === 'up';
974
1196
  const originRowIndex = this._rowIndex;
@@ -1008,7 +1230,7 @@ Table.prototype = {
1008
1230
 
1009
1231
  if (cell.rowSpan > 1) {
1010
1232
  cell.rowSpan -= 1;
1011
- spanCells.push({ cell: cell.cloneNode(false), index: logcalIndex });
1233
+ spanCells.push({ cell: /** @type {HTMLTableCellElement} */ (cell.cloneNode(false)), index: logcalIndex });
1012
1234
  }
1013
1235
  }
1014
1236
 
@@ -1047,13 +1269,25 @@ Table.prototype = {
1047
1269
  }
1048
1270
 
1049
1271
  if (!remove) {
1050
- this.setCellControllerPosition(positionResetElement || this._tdElement, true);
1272
+ this._setCellControllerPosition(positionResetElement || this._tdElement, true);
1051
1273
  } else {
1052
1274
  this._closeController();
1053
1275
  }
1054
- },
1276
+ }
1277
+
1278
+ /**
1279
+ * @description Edits a table cell(column), either adding, removing, or modifying the cell based on the provided option.
1280
+ * @param {?string} option The action to perform on the cell ("left"|"right"|null)
1281
+ * - null: to remove the cell
1282
+ * - left: to insert a new cell to the left
1283
+ * - right: to insert a new cell to the right
1284
+ * @param {?HTMLTableCellElement=} targetCell Target cell, (default: current selected cell)
1285
+ * @param {?HTMLTableCellElement=} positionResetElement The element to reset the position of (optional). This can be the cell that triggered the column edit.
1286
+ * @returns {HTMLTableCellElement} Target table cell
1287
+ */
1288
+ editCell(option, targetCell, positionResetElement) {
1289
+ if (targetCell) this.setCellInfo(targetCell, true);
1055
1290
 
1056
- editCell(option, positionResetElement) {
1057
1291
  const remove = !option;
1058
1292
  const left = option === 'left';
1059
1293
  const colSpan = this._current_colSpan;
@@ -1172,7 +1406,7 @@ Table.prototype = {
1172
1406
  }
1173
1407
 
1174
1408
  if (insertIndex !== null && cells.length > 0) {
1175
- newCell = CreateCells(cells[0].nodeName, 0, true);
1409
+ newCell = CreateCellsHTML(cells[0].nodeName);
1176
1410
  newCell = row.insertBefore(newCell, cells[insertIndex]);
1177
1411
  }
1178
1412
  }
@@ -1182,16 +1416,16 @@ Table.prototype = {
1182
1416
  if (colgroup) {
1183
1417
  const cols = colgroup.querySelectorAll('col');
1184
1418
  if (remove) {
1185
- domUtils.removeItem(cols[insertIndex]);
1419
+ dom.utils.removeItem(cols[insertIndex]);
1186
1420
  } else {
1187
1421
  let totalW = 0;
1188
1422
  for (let i = 0, len = cols.length, w; i < len; i++) {
1189
1423
  w = numbers.get(cols[i].style.width);
1190
- w -= Math.round((w * len * 0.1) / 2, CELL_DECIMAL_END);
1424
+ w -= Math.round((w * len * 0.1) / 2);
1191
1425
  totalW += w;
1192
1426
  cols[i].style.width = `${w}%`;
1193
1427
  }
1194
- const newCol = domUtils.createElement('col', { style: `width:${100 - totalW}%` });
1428
+ const newCol = dom.utils.createElement('col', { style: `width:${100 - totalW}%` });
1195
1429
  colgroup.insertBefore(newCol, cols[insertIndex]);
1196
1430
  }
1197
1431
  }
@@ -1199,12 +1433,12 @@ Table.prototype = {
1199
1433
  if (remove) {
1200
1434
  let removeFirst, removeEnd;
1201
1435
  for (let r = 0, rLen = removeCell.length, row; r < rLen; r++) {
1202
- row = removeCell[r].parentNode;
1203
- domUtils.removeItem(removeCell[r]);
1436
+ row = /** @type {HTMLTableRowElement} */ (removeCell[r].parentNode);
1437
+ dom.utils.removeItem(removeCell[r]);
1204
1438
  if (row.cells.length === 0) {
1205
- if (!removeFirst) removeFirst = domUtils.getArrayIndex(rows, row);
1206
- removeEnd = domUtils.getArrayIndex(rows, row);
1207
- domUtils.removeItem(row);
1439
+ if (!removeFirst) removeFirst = dom.utils.getArrayIndex(rows, row);
1440
+ removeEnd = dom.utils.getArrayIndex(rows, row);
1441
+ dom.utils.removeItem(row);
1208
1442
  }
1209
1443
  }
1210
1444
 
@@ -1215,19 +1449,339 @@ Table.prototype = {
1215
1449
 
1216
1450
  this._closeController();
1217
1451
  } else {
1218
- this.setCellControllerPosition(positionResetElement || this._tdElement, true);
1452
+ this._setCellControllerPosition(positionResetElement || this._tdElement, true);
1453
+ }
1454
+
1455
+ return positionResetElement || this._tdElement;
1456
+ }
1457
+
1458
+ /**
1459
+ * @description Updates the target table's cells with the data from the copied table.
1460
+ * @param {HTMLTableElement} copyTable The table containing the copied data.
1461
+ * @param {HTMLTableCellElement} targetTD The starting cell in the target table where data will be pasted.
1462
+ */
1463
+ pasteTableCellMatrix(copyTable, targetTD) {
1464
+ if (!copyTable || !targetTD) return;
1465
+
1466
+ // --- copy info ---
1467
+ const copyRows = copyTable.rows;
1468
+ let rowCnt = 0;
1469
+ const colIndexMap = [];
1470
+ for (let row = 0; row < copyRows.length; row++) {
1471
+ const cells = copyRows[row].cells;
1472
+ let logicalCol = 0;
1473
+
1474
+ for (let i = 0; i < cells.length; i++) {
1475
+ const cell = cells[i];
1476
+
1477
+ while (colIndexMap[row]?.[logicalCol]) {
1478
+ logicalCol++;
1479
+ }
1480
+
1481
+ const rowspan = cell.rowSpan || 1;
1482
+ const colspan = cell.colSpan || 1;
1483
+
1484
+ if (logicalCol === 0) {
1485
+ rowCnt += rowspan;
1486
+ }
1487
+
1488
+ // rowspan map
1489
+ for (let r = 0; r < rowspan; r++) {
1490
+ for (let c = 0; c < colspan; c++) {
1491
+ if (!colIndexMap[row + r]) colIndexMap[row + r] = [];
1492
+ colIndexMap[row + r][logicalCol + c] = true;
1493
+ }
1494
+ }
1495
+
1496
+ logicalCol += colspan;
1497
+ }
1498
+ }
1499
+
1500
+ let logicalColCount = 0;
1501
+ for (let i = 0, cells = copyRows[0].cells, len = cells.length; i < len; i++) {
1502
+ const cell = cells[i];
1503
+ logicalColCount += cell.colSpan || 1;
1504
+ }
1505
+
1506
+ const copyInfo = {
1507
+ rowCnt: rowCnt,
1508
+ logicalCellCnt: logicalColCount
1509
+ };
1510
+
1511
+ // --- target info ---
1512
+ this._deleteStyleSelectedCells();
1513
+ const originTable = targetTD.closest('table');
1514
+ const { cloneTable, clonedSelectedCells } = this.#cloneTable(originTable, [targetTD]);
1515
+
1516
+ const targetTable = cloneTable;
1517
+ targetTD = clonedSelectedCells[0];
1518
+ let targetRows = targetTable.rows;
1519
+ this.setTableInfo(targetTable);
1520
+ this.setCellInfo(targetTD, true);
1521
+
1522
+ const targetInfo = {
1523
+ physicalCellCnt: this._physical_cellCnt,
1524
+ logicalCellCnt: this._logical_cellCnt,
1525
+ rowCnt: this._rowCnt,
1526
+ rowInex: this._rowIndex,
1527
+ physicalCellIndex: this._physical_cellIndex,
1528
+ logicalCellIndex: this._logical_cellIndex,
1529
+ currentColSpan: this._current_colSpan,
1530
+ currentRowSpan: this._current_rowSpan
1531
+ };
1532
+
1533
+ // --- [expand] target table ---
1534
+ const addRowCnt = copyInfo.rowCnt - (targetInfo.rowCnt - (targetInfo.rowInex + 1)) - 1;
1535
+ const addColCnt = copyInfo.logicalCellCnt - (targetInfo.logicalCellCnt - (targetInfo.logicalCellIndex + 1)) - 1;
1536
+ targetInfo.rowCnt += addRowCnt;
1537
+ targetInfo.logicalCellCnt += addColCnt;
1538
+ targetInfo.physicalCellCnt += addColCnt;
1539
+
1540
+ if (addRowCnt > 0 || addColCnt > 0) {
1541
+ const lastRow = targetRows[targetRows.length - 1];
1542
+ const lastCell = lastRow.cells[lastRow.cells.length - 1];
1543
+ for (let i = 0; i < addRowCnt; i++) {
1544
+ this.editRow('down', lastCell);
1545
+ }
1546
+ for (let i = 0; i < addColCnt; i++) {
1547
+ this.editCell('right', lastCell);
1548
+ }
1549
+ targetRows = this._trElements = targetTable.rows;
1550
+ }
1551
+
1552
+ // --- [Un_merge] cells ---
1553
+ const startRowIndex = targetInfo.rowInex;
1554
+ const cellIndex = targetInfo.logicalCellIndex;
1555
+ const cellEndIndex = cellIndex + copyInfo.logicalCellCnt - 1;
1556
+ const unmergeCells = [];
1557
+ const un_mergeRowSpanMap = [];
1558
+
1559
+ for (let r = 0, len = startRowIndex + copyInfo.rowCnt; r < len; r++) {
1560
+ const cells = targetRows[r]?.cells;
1561
+ if (!cells) continue;
1562
+
1563
+ let logicalIndex = 0;
1564
+ let cellIndexInRow = 0;
1565
+
1566
+ for (let c = 0; c < cells.length; c++) {
1567
+ while (un_mergeRowSpanMap[r]?.[logicalIndex]) {
1568
+ logicalIndex++;
1569
+ }
1570
+
1571
+ const cell = cells[cellIndexInRow++];
1572
+ if (!cell) break;
1573
+
1574
+ const cs = cell.colSpan || 1;
1575
+ const rs = cell.rowSpan || 1;
1576
+ const logicalStart = logicalIndex;
1577
+ const logicalEnd = logicalIndex + cs - 1;
1578
+
1579
+ // rowSpan map
1580
+ if (rs > 1 || cs > 1) {
1581
+ for (let rsOffset = 1; rsOffset < rs; rsOffset++) {
1582
+ const rowIndex = r + rsOffset;
1583
+ if (!un_mergeRowSpanMap[rowIndex]) un_mergeRowSpanMap[rowIndex] = [];
1584
+ for (let csOffset = 0; csOffset < cs; csOffset++) {
1585
+ un_mergeRowSpanMap[rowIndex][logicalIndex + csOffset] = true;
1586
+ }
1587
+ }
1588
+ }
1589
+
1590
+ const isOverlap = logicalStart <= cellEndIndex && logicalEnd >= cellIndex;
1591
+ if (isOverlap && (cs > 1 || rs > 1)) {
1592
+ unmergeCells.push(cell);
1593
+ }
1594
+
1595
+ logicalIndex += cs;
1596
+ }
1597
+ }
1598
+
1599
+ if (unmergeCells.length > 0) {
1600
+ this.unmergeCells(unmergeCells, true);
1601
+ targetRows = this._trElements = targetTable.rows;
1219
1602
  }
1220
- },
1221
1603
 
1604
+ // --- [merge] cells ---
1605
+ const mergeGroups = [];
1606
+ const copyCowSpanMap = [];
1607
+ const targetRowSpanMap = [];
1608
+ for (let r = 0, len = copyInfo.rowCnt; r < len; r++) {
1609
+ const cells = copyRows[r]?.cells;
1610
+ if (!cells) break;
1611
+
1612
+ let copyIndex = 0;
1613
+ for (let c = 0; c < cells.length; c++) {
1614
+ const cell = cells[c];
1615
+ const cs = cell.colSpan || 1;
1616
+ const rs = cell.rowSpan || 1;
1617
+
1618
+ while (copyCowSpanMap[r]?.[copyIndex]) {
1619
+ copyIndex++;
1620
+ }
1621
+
1622
+ for (let rsOffset = 1; rsOffset < rs; rsOffset++) {
1623
+ const rowIndex = r + rsOffset;
1624
+ if (!copyCowSpanMap[rowIndex]) copyCowSpanMap[rowIndex] = [];
1625
+ for (let csOffset = 0; csOffset < cs; csOffset++) {
1626
+ copyCowSpanMap[rowIndex][copyIndex + csOffset] = true;
1627
+ }
1628
+ }
1629
+
1630
+ if (cs <= 1 && rs <= 1) {
1631
+ copyIndex += cs;
1632
+ continue;
1633
+ }
1634
+
1635
+ const cStart = copyIndex + targetInfo.logicalCellIndex;
1636
+ const cEnd = cStart + cs - 1;
1637
+ const mergeCells = [];
1638
+
1639
+ for (let targetR = targetInfo.rowInex + r, tRowCnt = targetR + rs, rowOffset = 0; targetR < tRowCnt; targetR++, rowOffset++) {
1640
+ const targetRow = targetRows[targetR];
1641
+ const targetCells = targetRow.cells;
1642
+
1643
+ let logicalIndex = 0;
1644
+ let targetIndex = 0;
1645
+
1646
+ while (targetIndex < targetCells.length && logicalIndex <= cEnd) {
1647
+ while (targetRowSpanMap[targetR]?.[logicalIndex]) {
1648
+ logicalIndex++;
1649
+ }
1650
+
1651
+ const tCell = targetCells[targetIndex++];
1652
+ const tcs = tCell.colSpan || 1;
1653
+ const trs = tCell.rowSpan || 1;
1654
+ const logicalStart = logicalIndex;
1655
+ const logicalEnd = logicalIndex + tcs - 1;
1656
+
1657
+ // rowSpan map
1658
+ if (trs > 1) {
1659
+ for (let rsOffset = 1; rsOffset < trs; rsOffset++) {
1660
+ const rIndex = targetR + rsOffset;
1661
+ if (!targetRowSpanMap[rIndex]) targetRowSpanMap[rIndex] = [];
1662
+ for (let i = 0; i < tcs; i++) {
1663
+ targetRowSpanMap[rIndex][logicalIndex + i] = true;
1664
+ }
1665
+ }
1666
+ }
1667
+
1668
+ if (logicalEnd >= cStart && logicalStart <= cEnd) {
1669
+ mergeCells.push(tCell);
1670
+ }
1671
+
1672
+ logicalIndex += tcs;
1673
+ }
1674
+ }
1675
+
1676
+ if (mergeCells.length > 0) {
1677
+ mergeGroups.push(mergeCells);
1678
+ }
1679
+
1680
+ copyIndex += cs;
1681
+ }
1682
+ }
1683
+
1684
+ if (mergeGroups.length > 0) {
1685
+ for (const mc of mergeGroups) {
1686
+ this._ref = null;
1687
+ this._trElements = targetTable.rows;
1688
+ this.mergeCells(mc, true);
1689
+ }
1690
+ targetRows = this._trElements = targetTable.rows;
1691
+ }
1692
+
1693
+ // --- [result] paste cell data ---
1694
+ const selectedCells = [];
1695
+ const rowSpanMap = [];
1696
+ for (let r = 0; r < copyInfo.rowCnt; r++) {
1697
+ const tr = targetRows[targetInfo.rowInex + r];
1698
+ const cr = copyRows[r];
1699
+ if (!tr || !cr) break;
1700
+
1701
+ const tCells = tr.cells;
1702
+ const cCells = cr.cells;
1703
+
1704
+ let tLogicalIndex = 0;
1705
+ let tIndex = 0;
1706
+ let cIndex = 0;
1707
+
1708
+ while (tIndex < tCells.length && cIndex < cCells.length && tLogicalIndex <= cellEndIndex) {
1709
+ while (rowSpanMap[r]?.[tLogicalIndex]) {
1710
+ tLogicalIndex++;
1711
+ }
1712
+
1713
+ const tCell = tCells[tIndex++];
1714
+ const cCell = cCells[cIndex];
1715
+ if (!tCell || !cCell) break;
1716
+
1717
+ const tcs = tCell.colSpan || 1;
1718
+ const trs = tCell.rowSpan || 1;
1719
+
1720
+ // rowSpan map
1721
+ if (trs > 1) {
1722
+ for (let rs = 1; rs < trs; rs++) {
1723
+ const rr = r + rs;
1724
+ if (!rowSpanMap[rr]) rowSpanMap[rr] = [];
1725
+ for (let cs = 0; cs < tcs; cs++) {
1726
+ rowSpanMap[rr][tLogicalIndex + cs] = true;
1727
+ }
1728
+ }
1729
+ }
1730
+
1731
+ if (tLogicalIndex >= cellIndex && tLogicalIndex + tcs - 1 <= cellEndIndex) {
1732
+ tCell.innerHTML = cCell.innerHTML;
1733
+ selectedCells.push(tCell);
1734
+ cIndex++;
1735
+ }
1736
+
1737
+ tLogicalIndex += tcs;
1738
+ }
1739
+ }
1740
+
1741
+ // replace table
1742
+ originTable.replaceWith(targetTable);
1743
+ this._closeTableSelectInfo();
1744
+ this.setTableInfo(targetTable);
1745
+
1746
+ // select cell
1747
+ this.selectCells(selectedCells);
1748
+ this._setMergeSplitButton();
1749
+ this._setUnMergeButton();
1750
+ this.#focusEdge(selectedCells[0]);
1751
+
1752
+ // history push
1753
+ this._historyPush();
1754
+ }
1755
+
1756
+ /**
1757
+ * @description Inserts a new row into the table at the specified index to it.
1758
+ * @param {HTMLTableElement} table The table element to insert the row into.
1759
+ * @param {number} rowIndex The index at which to insert the new row.
1760
+ * @param {number} cellCnt The number of cells to create in the new row.
1761
+ * @returns {HTMLTableRowElement} The newly inserted row element.
1762
+ */
1222
1763
  insertBodyRow(table, rowIndex, cellCnt) {
1223
1764
  const newRow = table.insertRow(rowIndex);
1224
- newRow.innerHTML = CreateCells('td', cellCnt, false);
1765
+ newRow.innerHTML = CreateCellsString('td', cellCnt);
1225
1766
  return newRow;
1226
- },
1767
+ }
1768
+
1769
+ /**
1770
+ * @description Merges the selected table cells into one cell by combining their contents and adjusting their row and column spans.
1771
+ * - This method removes the selected cells, consolidates their contents, and applies the appropriate row and column spans to the merged cell.
1772
+ * @param {HTMLTableCellElement[]} selectedCells Cells array
1773
+ * @param {boolean} [skipPostProcess=false] - If true, skips table cloning, cell re-selection, history stack push, and rendering.
1774
+ */
1775
+ mergeCells(selectedCells, skipPostProcess = false) {
1776
+ const originTable = selectedCells[0].closest('table');
1777
+ const { cloneTable, clonedSelectedCells } = skipPostProcess ? { cloneTable: originTable, clonedSelectedCells: selectedCells } : this.#cloneTable(originTable, selectedCells);
1778
+
1779
+ this.setTableInfo(cloneTable);
1780
+ selectedCells = clonedSelectedCells;
1781
+ this._ref = null;
1782
+ this._setMultiCells(selectedCells[0], dom.query.findVisualLastCell(selectedCells));
1227
1783
 
1228
- mergeCells() {
1229
1784
  const ref = this._ref;
1230
- const selectedCells = this._selectedCells;
1231
1785
  const mergeCell = selectedCells[0];
1232
1786
 
1233
1787
  let emptyRowFirst = null;
@@ -1239,17 +1793,17 @@ Table.prototype = {
1239
1793
 
1240
1794
  for (let i = 1, len = selectedCells.length, cell, ch; i < len; i++) {
1241
1795
  cell = selectedCells[i];
1242
- if (row !== cell.parentNode) row = cell.parentNode;
1796
+ if (row !== cell.parentNode) row = /** @type {HTMLTableRowElement} */ (cell.parentNode);
1243
1797
 
1244
1798
  ch = cell.children;
1245
1799
  for (let c = 0, cLen = ch.length; c < cLen; c++) {
1246
- if (this.format.isLine(ch[c]) && domUtils.isZeroWith(ch[c].textContent)) {
1247
- domUtils.removeItem(ch[c]);
1800
+ if (this.format.isLine(ch[c]) && dom.check.isZeroWidth(ch[c].textContent)) {
1801
+ dom.utils.removeItem(ch[c]);
1248
1802
  }
1249
1803
  }
1250
1804
 
1251
1805
  mergeHTML += cell.innerHTML;
1252
- domUtils.removeItem(cell);
1806
+ dom.utils.removeItem(cell);
1253
1807
 
1254
1808
  if (row.cells.length === 0) {
1255
1809
  if (!emptyRowFirst) emptyRowFirst = row;
@@ -1260,8 +1814,8 @@ Table.prototype = {
1260
1814
 
1261
1815
  if (emptyRowFirst) {
1262
1816
  const rows = this._trElements;
1263
- const rowIndexFirst = domUtils.getArrayIndex(rows, emptyRowFirst);
1264
- const rowIndexLast = domUtils.getArrayIndex(rows, emptyRowLast || emptyRowFirst);
1817
+ const rowIndexFirst = dom.utils.getArrayIndex(rows, emptyRowFirst);
1818
+ const rowIndexLast = dom.utils.getArrayIndex(rows, emptyRowLast || emptyRowFirst);
1265
1819
  const removeRows = [];
1266
1820
 
1267
1821
  for (let i = 0, cells; i <= rowIndexLast; i++) {
@@ -1281,7 +1835,7 @@ Table.prototype = {
1281
1835
  }
1282
1836
 
1283
1837
  for (let i = 0, len = removeRows.length; i < len; i++) {
1284
- domUtils.removeItem(removeRows[i]);
1838
+ dom.utils.removeItem(removeRows[i]);
1285
1839
  }
1286
1840
  }
1287
1841
 
@@ -1289,53 +1843,171 @@ Table.prototype = {
1289
1843
  mergeCell.colSpan = cs;
1290
1844
  mergeCell.rowSpan = rs;
1291
1845
 
1292
- this.setMergeSplitButton(true, false);
1293
- this.setController(mergeCell);
1846
+ if (skipPostProcess) return;
1294
1847
 
1295
- this.editor.focusEdge(mergeCell);
1296
- this._historyPush();
1297
- },
1848
+ // replace table
1849
+ originTable.replaceWith(cloneTable);
1850
+ this._closeTableSelectInfo();
1298
1851
 
1299
- toggleHeader() {
1300
- const btn = this.headerButton;
1301
- const active = domUtils.hasClass(btn, 'active');
1302
- const table = this._element;
1852
+ this._setMergeSplitButton();
1853
+ this._setController(mergeCell);
1303
1854
 
1304
- if (!active) {
1305
- const header = domUtils.createElement('THEAD');
1306
- header.innerHTML = '<tr>' + CreateCells('th', this._logical_cellCnt, false) + '</tr>';
1855
+ this.#focusEdge(mergeCell);
1856
+
1857
+ // history push
1858
+ this._historyPush();
1859
+ }
1860
+
1861
+ /**
1862
+ * @description Unmerges a table cell that has been merged using rowspan and/or colspan.
1863
+ * @param {HTMLTableCellElement[]} selectedCells - Cells array
1864
+ * @param {boolean} [skipPostProcess=false] - If true, skips table cloning, cell re-selection, history stack push, and rendering.
1865
+ */
1866
+ unmergeCells(selectedCells, skipPostProcess = false) {
1867
+ if (!selectedCells?.length) return;
1868
+
1869
+ const originTable = selectedCells[0].closest('table');
1870
+ const { cloneTable, clonedSelectedCells } = skipPostProcess ? { cloneTable: originTable, clonedSelectedCells: selectedCells } : this.#cloneTable(originTable, selectedCells);
1871
+
1872
+ this._ref = null;
1873
+ this.setTableInfo(cloneTable);
1874
+ selectedCells = clonedSelectedCells;
1875
+
1876
+ let firstCell = selectedCells[0];
1877
+ let lastCell = dom.query.findVisualLastCell(selectedCells);
1878
+ let newLastCell = null;
1879
+
1880
+ const table = firstCell.closest('table');
1881
+ const rows = table.rows;
1882
+
1883
+ for (const cell of selectedCells) {
1884
+ const tr = /** @type {HTMLTableRowElement} */ (cell.parentElement);
1885
+ const rowIndex = tr.rowIndex;
1886
+ const colIndex = cell.cellIndex;
1887
+ const rowspan = cell.rowSpan;
1888
+ const colspan = cell.colSpan;
1889
+
1890
+ if (rowspan === 1 && colspan === 1) continue;
1891
+
1892
+ this.setCellInfo(cell, true);
1893
+
1894
+ const originalHTML = cell.innerHTML;
1895
+ cell.remove();
1896
+
1897
+ for (let r = 0; r < rowspan; r++) {
1898
+ const targetRow = rows[rowIndex + r];
1899
+
1900
+ for (let c = 0; c < colspan; c++) {
1901
+ const newCell = CreateCellsHTML('td');
1902
+
1903
+ if (r === 0 && c === 0) {
1904
+ if (firstCell === cell) firstCell = newCell;
1905
+ if (lastCell === cell) lastCell = newCell;
1906
+ newCell.innerHTML = originalHTML;
1907
+ targetRow.insertBefore(newCell, targetRow.cells[colIndex]);
1908
+ } else {
1909
+ targetRow.insertBefore(newCell, targetRow.cells[colIndex + c]);
1910
+ newLastCell = newCell;
1911
+ }
1912
+ }
1913
+ }
1914
+ }
1915
+
1916
+ this._selectedCells = null;
1917
+
1918
+ if (skipPostProcess) return;
1919
+
1920
+ // replace table
1921
+ originTable.replaceWith(cloneTable);
1922
+ this._closeTableSelectInfo();
1923
+ this.setTableInfo(cloneTable);
1924
+
1925
+ // set info
1926
+ if (firstCell !== lastCell) {
1927
+ lastCell = !newLastCell || lastCell.closest('tr').rowIndex > newLastCell.closest('tr').rowIndex || lastCell.cellIndex > newLastCell.cellIndex ? lastCell : newLastCell;
1928
+ this._setMultiCells(firstCell, lastCell);
1929
+ this._selectedCells = Array.from(table.querySelectorAll('.se-selected-table-cell'));
1930
+ } else {
1931
+ this.setCellInfo(lastCell, true);
1932
+ }
1933
+
1934
+ this._fixedCell = firstCell;
1935
+ this._selectedCell = lastCell;
1936
+ dom.utils.addClass(lastCell, 'se-selected-cell-focus');
1937
+
1938
+ this._setUnMergeButton();
1939
+ this.controller_cell.resetPosition(lastCell);
1940
+
1941
+ // history push
1942
+ this._historyPush();
1943
+ }
1944
+
1945
+ /**
1946
+ * @description Find merged cells
1947
+ * @param {HTMLTableCellElement[]} cells - Cells array
1948
+ */
1949
+ findMergedCells(cells) {
1950
+ const mergedCells = [];
1951
+ cells?.forEach((cell) => {
1952
+ if (cell && (cell.rowSpan > 1 || cell.colSpan > 1)) {
1953
+ mergedCells.push(cell);
1954
+ }
1955
+ });
1956
+ return mergedCells;
1957
+ }
1958
+
1959
+ /**
1960
+ * @description Toggles the visibility of the table header (`<thead>`). If the header is present, it is removed; if absent, it is added.
1961
+ */
1962
+ toggleHeader() {
1963
+ const btn = this.headerButton;
1964
+ const active = dom.utils.hasClass(btn, 'active');
1965
+ const table = this._element;
1966
+
1967
+ if (!active) {
1968
+ const header = dom.utils.createElement('THEAD');
1969
+ header.innerHTML = '<tr>' + CreateCellsString('th', this._logical_cellCnt) + '</tr>';
1307
1970
  table.insertBefore(header, table.firstElementChild);
1308
1971
  } else {
1309
- domUtils.removeItem(table.querySelector('thead'));
1972
+ dom.utils.removeItem(table.querySelector('thead'));
1310
1973
  }
1311
1974
 
1312
- domUtils.toggleClass(btn, 'active');
1975
+ dom.utils.toggleClass(btn, 'active');
1313
1976
 
1314
1977
  if (/TH/i.test(this._tdElement.nodeName)) {
1315
1978
  this._closeController();
1316
1979
  } else {
1317
- this.setCellControllerPosition(this._tdElement, false);
1980
+ this._setCellControllerPosition(this._tdElement, false);
1318
1981
  }
1319
- },
1982
+ }
1320
1983
 
1984
+ /**
1985
+ * @description Toggles the visibility of the table caption (`<caption>`). If the caption is present, it is removed; if absent, it is added.
1986
+ */
1321
1987
  toggleCaption() {
1322
1988
  const btn = this.captionButton;
1323
- const active = domUtils.hasClass(btn, 'active');
1989
+ const active = dom.utils.hasClass(btn, 'active');
1324
1990
  const table = this._element;
1325
1991
 
1326
1992
  if (!active) {
1327
- const caption = domUtils.createElement('CAPTION', { class: `se-table-caption-${this.captionPosition}` });
1993
+ const caption = dom.utils.createElement('CAPTION', { class: `se-table-caption-${this.captionPosition}` });
1328
1994
  caption.innerHTML = '<div><br></div>';
1329
1995
  table.insertBefore(caption, table.firstElementChild);
1330
1996
  } else {
1331
- domUtils.removeItem(table.querySelector('caption'));
1997
+ dom.utils.removeItem(table.querySelector('caption'));
1332
1998
  }
1333
1999
 
1334
- domUtils.toggleClass(btn, 'active');
1335
- this.setCellControllerPosition(this._tdElement, false);
1336
- },
2000
+ dom.utils.toggleClass(btn, 'active');
2001
+ this._setCellControllerPosition(this._tdElement, false);
2002
+ }
1337
2003
 
1338
- setTableStyle(styles, ondisplay) {
2004
+ /**
2005
+ * @private
2006
+ * @description Updates table styles.
2007
+ * @param {string} styles - Styles to update.
2008
+ * @param {boolean} ondisplay - Whether to update display.
2009
+ */
2010
+ _setTableStyle(styles, ondisplay) {
1339
2011
  if (styles.includes('width')) {
1340
2012
  const targets = this._figure;
1341
2013
  if (!targets) return;
@@ -1351,92 +2023,114 @@ Table.prototype = {
1351
2023
  if (!ondisplay) targets.style.width = '100%';
1352
2024
  }
1353
2025
 
1354
- domUtils.changeElement(this.resizeButton.firstElementChild, sizeIcon);
1355
- domUtils.changeTxt(this.resizeText, text);
2026
+ dom.utils.changeElement(this.resizeButton.firstElementChild, sizeIcon);
2027
+ dom.utils.changeTxt(this.resizeText, text);
1356
2028
  }
1357
2029
 
1358
2030
  if (styles.includes('column')) {
1359
2031
  if (!this._fixedColumn) {
1360
- domUtils.removeClass(this._element, 'se-table-layout-fixed');
1361
- domUtils.addClass(this._element, 'se-table-layout-auto');
1362
- domUtils.removeClass(this.columnFixedButton, 'active');
2032
+ dom.utils.removeClass(this._element, 'se-table-layout-fixed');
2033
+ dom.utils.addClass(this._element, 'se-table-layout-auto');
2034
+ dom.utils.removeClass(this.columnFixedButton, 'active');
1363
2035
  } else {
1364
- domUtils.removeClass(this._element, 'se-table-layout-auto');
1365
- domUtils.addClass(this._element, 'se-table-layout-fixed');
1366
- domUtils.addClass(this.columnFixedButton, 'active');
2036
+ dom.utils.removeClass(this._element, 'se-table-layout-auto');
2037
+ dom.utils.addClass(this._element, 'se-table-layout-fixed');
2038
+ dom.utils.addClass(this.columnFixedButton, 'active');
1367
2039
  }
1368
2040
  }
1369
- },
2041
+ }
1370
2042
 
1371
- setMergeSplitButton(fixedCell, selectedCell) {
1372
- if (!selectedCell || !selectedCell || fixedCell === selectedCell) {
2043
+ /**
2044
+ * @private
2045
+ * @description Sets the merge/split button visibility.
2046
+ */
2047
+ _setMergeSplitButton() {
2048
+ if (!this._ref) {
1373
2049
  this.splitButton.style.display = 'block';
1374
2050
  this.mergeButton.style.display = 'none';
1375
2051
  } else {
1376
2052
  this.splitButton.style.display = 'none';
1377
2053
  this.mergeButton.style.display = 'block';
1378
2054
  }
1379
- },
2055
+ }
1380
2056
 
1381
2057
  /**
1382
- * @override fileManager
1383
- * @param {Element} target Target element
2058
+ * @private
2059
+ * @description Sets the unmerge button visibility.
1384
2060
  */
1385
- select(target) {
1386
- this._figureOpen(target);
1387
-
1388
- const targetWidth = this._figure?.style.width || '100%';
1389
- this._maxWidth = targetWidth === '100%';
1390
- this._fixedColumn = domUtils.hasClass(target, 'se-table-layout-fixed') || target.style.tableLayout === 'fixed';
1391
- this.setTableStyle(this._maxWidth ? 'width|column' : 'width', true);
1392
-
1393
- if (_DragHandle.get('__overInfo') === ON_OVER_COMPONENT) return;
1394
-
1395
- if (!this._tdElement) return;
1396
- this.setCellInfo(this._tdElement, true);
1397
-
1398
- // controller open
1399
- const figureEl = domUtils.getParentElement(target, domUtils.isFigure);
1400
- this.controller_table.open(figureEl, null, { isWWTarget: false, initMethod: null, addOffset: null });
1401
-
1402
- const addOffset = !this.cellControllerTop ? null : this.controller_table.form.style.display === 'block' ? { left: this.controller_table.form.offsetWidth + 2 } : null;
1403
- this.controller_cell.open(this._tdElement, this.cellControllerTop ? figureEl : null, { isWWTarget: false, initMethod: null, addOffset: addOffset });
1404
- },
2061
+ _setUnMergeButton() {
2062
+ if (this.findMergedCells(!this._selectedCells?.length ? [this._fixedCell] : this._selectedCells).length > 0) {
2063
+ this.unmergeButton.disabled = false;
2064
+ } else {
2065
+ this.unmergeButton.disabled = true;
2066
+ }
2067
+ }
1405
2068
 
1406
- setController(tdElement) {
2069
+ /**
2070
+ * @private
2071
+ * @description Sets the controller position for a cell.
2072
+ * @param {HTMLTableCellElement} tdElement - The target table cell.
2073
+ */
2074
+ _setController(tdElement) {
1407
2075
  if (!this.selection.get().isCollapsed && !this._selectedCell) {
1408
2076
  this._deleteStyleSelectedCells();
1409
2077
  return;
1410
2078
  }
1411
2079
 
2080
+ this._setUnMergeButton();
2081
+
1412
2082
  this._tdElement = tdElement;
1413
- domUtils.addClass(tdElement, 'se-selected-cell-focus');
1414
- const tableElement = this._element || this._selectedTable || domUtils.getParentElement(tdElement, 'TABLE');
1415
- this.component.select(tableElement, Table.key, true);
1416
- },
2083
+ if (this._fixedCell === tdElement) dom.utils.addClass(tdElement, 'se-selected-cell-focus');
2084
+ if (!this._selectedCells?.length) this._selectedCells = [tdElement];
2085
+ const tableElement = this._selectedTable || this._element || dom.query.getParentElement(tdElement, 'TABLE');
2086
+ this.component.select(tableElement, Table.key, { isInput: true });
2087
+ }
1417
2088
 
1418
- setCellControllerPosition(tdElement, reset) {
2089
+ /**
2090
+ * @private
2091
+ * @description Sets the position of the cell controller.
2092
+ * @param {HTMLTableCellElement} tdElement - The target table cell.
2093
+ * @param {boolean} reset - Whether to reset the controller position.
2094
+ */
2095
+ _setCellControllerPosition(tdElement, reset) {
1419
2096
  this.setCellInfo(tdElement, reset);
1420
- this.controller_cell.resetPosition(this.cellControllerTop ? domUtils.getParentElement(tdElement, domUtils.isTable) : tdElement);
1421
- },
2097
+ this.controller_cell.resetPosition(this.cellControllerTop ? dom.query.getParentElement(tdElement, dom.check.isTable) : tdElement);
2098
+ }
1422
2099
 
2100
+ /**
2101
+ * @private
2102
+ * @description Adds a new entry to the history stack.
2103
+ */
1423
2104
  _historyPush() {
1424
2105
  this._deleteStyleSelectedCells();
1425
2106
  this.history.push(false);
1426
2107
  this._recallStyleSelectedCells();
1427
- },
2108
+ }
1428
2109
 
2110
+ /**
2111
+ * @private
2112
+ * @description Opens the figure.
2113
+ * @param {Node} target - The target figure element.
2114
+ */
1429
2115
  _figureOpen(target) {
1430
2116
  this.figure.open(target, { nonResizing: true, nonSizeInfo: true, nonBorder: true, figureTarget: true, __fileManagerInfo: false });
1431
- },
2117
+ }
1432
2118
 
2119
+ /**
2120
+ * @private
2121
+ * @description Starts resizing a table cell.
2122
+ * @param {HTMLElement} col The column element.
2123
+ * @param {number} startX The starting X position.
2124
+ * @param {number} startWidth The initial width of the column.
2125
+ * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2126
+ */
1433
2127
  _startCellResizing(col, startX, startWidth, isLeftEdge) {
1434
2128
  this._setResizeLinePosition(this._figure, this._tdElement, this._resizeLinePrev, isLeftEdge);
1435
2129
  this._resizeLinePrev.style.display = 'block';
1436
2130
  const prevValue = col.style.width;
1437
- const nextCol = col.nextElementSibling;
2131
+ const nextCol = /** @type {HTMLElement} */ (col.nextElementSibling);
1438
2132
  const nextColPrevValue = nextCol.style.width;
1439
- const realWidth = domUtils.hasClass(this._element, 'se-table-layout-fixed') ? nextColPrevValue : converter.getWidthInPercentage(col);
2133
+ const realWidth = dom.utils.hasClass(this._element, 'se-table-layout-fixed') ? nextColPrevValue : converter.getWidthInPercentage(col);
1440
2134
 
1441
2135
  if (_DragHandle.get('__dragHandler')) _DragHandle.get('__dragHandler').style.display = 'none';
1442
2136
  this._addResizeGlobalEvents(
@@ -1457,16 +2151,31 @@ Table.prototype = {
1457
2151
  () => {
1458
2152
  this.__removeGlobalEvents();
1459
2153
  this.history.push(true);
1460
- // figure reopen
1461
- this.component.select(this._element, Table.key, true);
2154
+ this.component.select(this._element, Table.key, { isInput: true });
1462
2155
  },
1463
2156
  (e) => {
1464
2157
  this._stopResize(col, prevValue, 'width', e);
1465
2158
  this._stopResize(nextCol, nextColPrevValue, 'width', e);
1466
2159
  }
1467
2160
  );
1468
- },
2161
+ }
1469
2162
 
2163
+ /**
2164
+ * @private
2165
+ * @description Resizes a table cell.
2166
+ * @param {HTMLElement} col The column element.
2167
+ * @param {HTMLElement} nextCol The next column element.
2168
+ * @param {HTMLElement} figure The table figure element.
2169
+ * @param {HTMLElement} tdEl The table cell element.
2170
+ * @param {HTMLElement} resizeLine The resize line element.
2171
+ * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2172
+ * @param {number} startX The starting X position.
2173
+ * @param {number} startWidth The initial width of the column.
2174
+ * @param {number} prevWidthPercent The previous width percentage.
2175
+ * @param {number} nextColWidthPercent The next column's width percentage.
2176
+ * @param {number} tableWidth The total width of the table.
2177
+ * @param {MouseEvent} e The mouse event.
2178
+ */
1470
2179
  _cellResize(col, nextCol, figure, tdEl, resizeLine, isLeftEdge, startX, startWidth, prevWidthPercent, nextColWidthPercent, tableWidth, e) {
1471
2180
  const deltaX = e.clientX - startX;
1472
2181
  const newWidthPx = startWidth + deltaX;
@@ -1478,8 +2187,15 @@ Table.prototype = {
1478
2187
  nextCol.style.width = `${nextColWidthPercent + delta}%`;
1479
2188
  this._setResizeLinePosition(figure, tdEl, resizeLine, isLeftEdge);
1480
2189
  }
1481
- },
2190
+ }
1482
2191
 
2192
+ /**
2193
+ * @private
2194
+ * @description Starts resizing a table row.
2195
+ * @param {HTMLElement} row The table row element.
2196
+ * @param {number} startY The starting Y position.
2197
+ * @param {number} startHeight The initial height of the row.
2198
+ */
1483
2199
  _startRowResizing(row, startY, startHeight) {
1484
2200
  this._setResizeRowPosition(this._figure, row, this._resizeLinePrev);
1485
2201
  this._resizeLinePrev.style.display = 'block';
@@ -1488,20 +2204,36 @@ Table.prototype = {
1488
2204
  this._addResizeGlobalEvents(
1489
2205
  this._rowResize.bind(this, row, this._figure, this._resizeLine, startY, startHeight),
1490
2206
  () => {
1491
- this.__removeGlobalEvents(this);
2207
+ this.__removeGlobalEvents();
1492
2208
  this.history.push(true);
1493
2209
  },
1494
2210
  this._stopResize.bind(this, row, prevValue, 'height')
1495
2211
  );
1496
- },
2212
+ }
1497
2213
 
2214
+ /**
2215
+ * @private
2216
+ * @description Resizes a table row.
2217
+ * @param {HTMLElement} row The table row element.
2218
+ * @param {HTMLElement} figure The table figure element.
2219
+ * @param {HTMLElement} resizeLine The resize line element.
2220
+ * @param {number} startY The starting Y position.
2221
+ * @param {number} startHeight The initial height of the row.
2222
+ * @param {MouseEvent} e The mouse event.
2223
+ */
1498
2224
  _rowResize(row, figure, resizeLine, startY, startHeight, e) {
1499
2225
  const deltaY = e.clientY - startY;
1500
2226
  const newHeightPx = startHeight + deltaY;
1501
2227
  row.style.height = `${newHeightPx}px`;
1502
2228
  this._setResizeRowPosition(figure, row, resizeLine);
1503
- },
2229
+ }
1504
2230
 
2231
+ /**
2232
+ * @private
2233
+ * @description Starts resizing the table figure.
2234
+ * @param {number} startX The starting X position.
2235
+ * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2236
+ */
1505
2237
  _startFigureResizing(startX, isLeftEdge) {
1506
2238
  const figure = this._figure;
1507
2239
  this._setResizeLinePosition(figure, figure, this._resizeLinePrev, isLeftEdge);
@@ -1514,12 +2246,23 @@ Table.prototype = {
1514
2246
  () => {
1515
2247
  this.__removeGlobalEvents();
1516
2248
  // figure reopen
1517
- this.component.select(this._element, Table.key, true);
2249
+ this.component.select(this._element, Table.key, { isInput: true });
1518
2250
  },
1519
2251
  this._stopResize.bind(this, figure, figure.style.width, 'width')
1520
2252
  );
1521
- },
2253
+ }
1522
2254
 
2255
+ /**
2256
+ * @private
2257
+ * @description Resizes the table figure.
2258
+ * @param {HTMLElement} figure The table figure element.
2259
+ * @param {HTMLElement} resizeLine The resize line element.
2260
+ * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2261
+ * @param {number} startX The starting X position.
2262
+ * @param {number} startWidth The initial width of the figure.
2263
+ * @param {number} constNum A constant number used for width calculation.
2264
+ * @param {MouseEvent} e The mouse event.
2265
+ */
1523
2266
  _figureResize(figure, resizeLine, isLeftEdge, startX, startWidth, constNum, e) {
1524
2267
  const deltaX = isLeftEdge ? startX - e.clientX : e.clientX - startX;
1525
2268
  const newWidthPx = startWidth + deltaX;
@@ -1529,78 +2272,128 @@ Table.prototype = {
1529
2272
  figure.style.width = `${newWidthPercent}%`;
1530
2273
  this._setResizeLinePosition(figure, figure, resizeLine, isLeftEdge);
1531
2274
  }
1532
- },
2275
+ }
1533
2276
 
2277
+ /**
2278
+ * @private
2279
+ * @description Sets the resize line position.
2280
+ * @param {HTMLElement} figure The table figure element.
2281
+ * @param {HTMLElement} target The target element.
2282
+ * @param {HTMLElement} resizeLine The resize line element.
2283
+ * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2284
+ */
1534
2285
  _setResizeLinePosition(figure, target, resizeLine, isLeftEdge) {
1535
2286
  const tdOffset = this.offset.getLocal(target);
1536
2287
  const tableOffset = this.offset.getLocal(figure);
1537
2288
  resizeLine.style.left = `${tdOffset.left + (isLeftEdge ? 0 : target.offsetWidth)}px`;
1538
2289
  resizeLine.style.top = `${tableOffset.top}px`;
1539
2290
  resizeLine.style.height = `${figure.offsetHeight}px`;
1540
- },
2291
+ }
1541
2292
 
2293
+ /**
2294
+ * @private
2295
+ * @description Sets the resize row position.
2296
+ * @param {HTMLElement} figure The table figure element.
2297
+ * @param {HTMLElement} target The target row element.
2298
+ * @param {HTMLElement} resizeLine The resize line element.
2299
+ */
1542
2300
  _setResizeRowPosition(figure, target, resizeLine) {
1543
2301
  const rowOffset = this.offset.getLocal(target);
1544
2302
  const tableOffset = this.offset.getLocal(figure);
1545
2303
  resizeLine.style.top = `${rowOffset.top + target.offsetHeight}px`;
1546
2304
  resizeLine.style.left = `${tableOffset.left}px`;
1547
2305
  resizeLine.style.width = `${figure.offsetWidth}px`;
1548
- },
2306
+ }
1549
2307
 
2308
+ /**
2309
+ * @private
2310
+ * @description Stops resizing the table.
2311
+ * @param {HTMLElement} target The target element.
2312
+ * @param {string} prevValue The previous style value.
2313
+ * @param {string} styleProp The CSS property being changed.
2314
+ * @param {KeyboardEvent} e The keyboard event.
2315
+ */
1550
2316
  _stopResize(target, prevValue, styleProp, e) {
1551
- if (e.keyCode !== 27) return;
2317
+ if (!keyCodeMap.isEsc(e.code)) return;
1552
2318
  this.__removeGlobalEvents();
1553
2319
  target.style[styleProp] = prevValue;
1554
2320
  // figure reopen
1555
2321
  if (styleProp === 'width') {
1556
- this.component.select(this._element, Table.key, true);
2322
+ this.component.select(this._element, Table.key, { isInput: true });
1557
2323
  }
1558
- },
2324
+ }
1559
2325
 
2326
+ /**
2327
+ * @private
2328
+ * @description Deletes styles from selected table cells.
2329
+ */
1560
2330
  _deleteStyleSelectedCells() {
1561
- domUtils.removeClass([this._fixedCell, this._selectedCell], 'se-selected-cell-focus');
1562
- if (this._selectedTable) {
1563
- const selectedCells = this._selectedTable.querySelectorAll('.se-selected-table-cell');
2331
+ dom.utils.removeClass([this._fixedCell, this._selectedCell], 'se-selected-cell-focus');
2332
+ const table = this._fixedCell?.closest('table');
2333
+ if (table) {
2334
+ const selectedCells = table.querySelectorAll('.se-selected-table-cell');
1564
2335
  for (let i = 0, len = selectedCells.length; i < len; i++) {
1565
- domUtils.removeClass(selectedCells[i], 'se-selected-table-cell');
2336
+ dom.utils.removeClass(selectedCells[i], 'se-selected-table-cell');
1566
2337
  }
1567
2338
  }
1568
- },
2339
+ }
1569
2340
 
2341
+ /**
2342
+ * @private
2343
+ * @description Restores styles for selected table cells.
2344
+ */
1570
2345
  _recallStyleSelectedCells() {
1571
2346
  if (this._selectedCells) {
1572
2347
  const selectedCells = this._selectedCells;
1573
2348
  for (let i = 0, len = selectedCells.length; i < len; i++) {
1574
- domUtils.addClass(selectedCells[i], 'se-selected-table-cell');
2349
+ dom.utils.addClass(selectedCells[i], 'se-selected-table-cell');
1575
2350
  }
1576
2351
  }
1577
- },
2352
+ }
1578
2353
 
2354
+ /**
2355
+ * @private
2356
+ * @description Adds global event listeners for resizing.
2357
+ * @param {(...args: *) => void} resizeFn The function handling the resize event.
2358
+ * @param {(...args: *) => void} stopFn The function handling the stop event.
2359
+ * @param {(...args: *) => void} keyDownFn The function handling the keydown event.
2360
+ */
1579
2361
  _addResizeGlobalEvents(resizeFn, stopFn, keyDownFn) {
1580
2362
  this.__globalEvents.resize = this.eventManager.addGlobalEvent('mousemove', resizeFn, false);
1581
2363
  this.__globalEvents.resizeStop = this.eventManager.addGlobalEvent('mouseup', stopFn, false);
1582
2364
  this.__globalEvents.resizeKeyDown = this.eventManager.addGlobalEvent('keydown', keyDownFn, false);
1583
2365
  this._resizing = true;
1584
- },
2366
+ }
1585
2367
 
2368
+ /**
2369
+ * @private
2370
+ * @description Enables or disables editor mode.
2371
+ * @param {boolean} enabled Whether to enable or disable the editor.
2372
+ */
1586
2373
  _toggleEditor(enabled) {
1587
2374
  const wysiwyg = this.editor.frameContext.get('wysiwyg');
1588
- wysiwyg.setAttribute('contenteditable', enabled);
1589
- if (enabled) domUtils.removeClass(wysiwyg, 'se-disabled');
1590
- else domUtils.addClass(wysiwyg, 'se-disabled');
1591
- },
2375
+ wysiwyg.setAttribute('contenteditable', enabled.toString());
2376
+ if (enabled) dom.utils.removeClass(wysiwyg, 'se-disabled');
2377
+ else dom.utils.addClass(wysiwyg, 'se-disabled');
2378
+ }
1592
2379
 
2380
+ /**
2381
+ * @private
2382
+ * @description Updates control properties.
2383
+ * @param {string} type The type of control property.
2384
+ */
1593
2385
  _setCtrlProps(type) {
1594
2386
  this._typeCache = type;
1595
2387
  const isTable = type === 'table';
1596
2388
  const targets = isTable ? [this._element] : this._selectedCells;
1597
2389
  if (!targets || targets.length === 0) return;
1598
2390
 
1599
- const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike } = this.propTargets;
2391
+ const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, cell_alignment_table_text, font_bold, font_underline, font_italic, font_strike } = this.propTargets;
1600
2392
  const { border, backgroundColor, color, textAlign, verticalAlign, fontWeight, textDecoration, fontStyle } = _w.getComputedStyle(targets[0]);
1601
2393
  const cellBorder = this._getBorderStyle(border);
1602
2394
 
1603
- cell_alignment.querySelector('[data-value="justify"]').style.display = isTable ? 'none' : '';
2395
+ /** @type {HTMLElement} */ (cell_alignment.querySelector('[data-value="justify"]')).style.display = isTable ? 'none' : '';
2396
+ cell_alignment_table_text.style.display = isTable ? '' : 'none';
1604
2397
  if (isTable) cell_alignment_vertical.style.display = 'none';
1605
2398
  else cell_alignment_vertical.style.display = '';
1606
2399
 
@@ -1632,7 +2425,7 @@ Table.prototype = {
1632
2425
  if (fontColor !== converter.rgb2hex(color)) fontColor = '';
1633
2426
  if (align !== (isTable ? this._figure?.style.float : textAlign)) align = '';
1634
2427
  if (align_v && align_v !== verticalAlign) align_v = '';
1635
- if (bold && bold !== /.+/.test(fontWeight)) bold = '';
2428
+ if (bold && bold !== /.+/.test(fontWeight)) bold = false;
1636
2429
  if (underline && underline !== /underline/i.test(textDecoration)) underline = false;
1637
2430
  if (strike && strike !== /line-through/i.test(textDecoration)) strike = false;
1638
2431
  if (italic && italic !== /italic/i.test(fontStyle)) italic = false;
@@ -1644,7 +2437,7 @@ Table.prototype = {
1644
2437
  // border - format
1645
2438
  border_format.firstElementChild.innerHTML = this.icons[BORDER_FORMATS[targets.length === 1 ? 'outside' : 'all']];
1646
2439
  border_format.setAttribute('se-border-format', 'all');
1647
- domUtils.removeClass(border_format, 'active');
2440
+ dom.utils.removeClass(border_format, 'active');
1648
2441
 
1649
2442
  // border - styles
1650
2443
  b_style = b_style || BORDER_LIST[0];
@@ -1658,43 +2451,64 @@ Table.prototype = {
1658
2451
  font_color.value = font_color.style.borderColor = fontColor;
1659
2452
 
1660
2453
  // font style
1661
- if (bold) domUtils.addClass(font_bold, 'on');
1662
- if (underline) domUtils.addClass(font_underline, 'on');
1663
- if (strike) domUtils.addClass(font_strike, 'on');
1664
- if (italic) domUtils.addClass(font_italic, 'on');
2454
+ if (bold) dom.utils.addClass(font_bold, 'on');
2455
+ if (underline) dom.utils.addClass(font_underline, 'on');
2456
+ if (strike) dom.utils.addClass(font_strike, 'on');
2457
+ if (italic) dom.utils.addClass(font_italic, 'on');
1665
2458
 
1666
2459
  // align
1667
2460
  this._setAlignProps(cell_alignment, (this._propsAlignCache = align), true);
1668
2461
  this._setAlignProps(cell_alignment_vertical, (this._propsVerticalAlignCache = align_v), true);
1669
- },
2462
+ }
1670
2463
 
2464
+ /**
2465
+ * @private
2466
+ * @description Sets text alignment properties.
2467
+ * @param {Element} el The element to apply alignment to.
2468
+ * @param {string} align The alignment value.
2469
+ * @param {boolean} reset Whether to reset the alignment.
2470
+ */
1671
2471
  _setAlignProps(el, align, reset) {
1672
- domUtils.removeClass(el.querySelectorAll('button'), 'on');
2472
+ dom.utils.removeClass(el.querySelectorAll('button'), 'on');
1673
2473
 
1674
2474
  if (!reset && el.getAttribute('se-cell-align') === align) {
1675
2475
  el.setAttribute('se-cell-align', '');
1676
2476
  return;
1677
2477
  }
1678
2478
 
1679
- domUtils.addClass(el.querySelector(`[data-value="${align}"]`), 'on');
2479
+ dom.utils.addClass(el.querySelector(`[data-value="${align}"]`), 'on');
1680
2480
  el.setAttribute('se-cell-align', align);
1681
- },
2481
+ }
1682
2482
 
2483
+ /**
2484
+ * @private
2485
+ * @description Disables or enables border properties.
2486
+ * @param {boolean} disabled Whether to disable or enable border properties.
2487
+ */
1683
2488
  _disableBorderProps(disabled) {
1684
2489
  const { border_color, border_width, palette_border_button } = this.propTargets;
1685
2490
  if (disabled) {
1686
- border_color.setAttribute('disabled', true);
1687
- border_width.setAttribute('disabled', true);
1688
- palette_border_button.setAttribute('disabled', true);
1689
- border_width.setAttribute('disabled', true);
2491
+ border_color.disabled = true;
2492
+ border_width.disabled = true;
2493
+ palette_border_button.disabled = true;
2494
+ border_width.disabled = true;
1690
2495
  } else {
1691
- border_color.removeAttribute('disabled');
1692
- border_width.removeAttribute('disabled');
1693
- palette_border_button.removeAttribute('disabled');
1694
- border_width.removeAttribute('disabled');
2496
+ border_color.disabled = false;
2497
+ border_width.disabled = false;
2498
+ palette_border_button.disabled = false;
2499
+ border_width.disabled = false;
1695
2500
  }
1696
- },
2501
+ }
1697
2502
 
2503
+ /**
2504
+ * @private
2505
+ * @description Gets the border style.
2506
+ * @param {string} borderStyle The border style string.
2507
+ * @returns {{w: string, s: string, c: string}} The parsed border style object.
2508
+ * - w: The border width.
2509
+ * - s: The border style.
2510
+ * - c: The border color.
2511
+ */
1698
2512
  _getBorderStyle(borderStyle) {
1699
2513
  const parts = borderStyle.split(/\s(?![^()]*\))/);
1700
2514
  let w = '',
@@ -1722,15 +2536,20 @@ Table.prototype = {
1722
2536
  }
1723
2537
 
1724
2538
  return { w, s, c: converter.rgb2hex(c) };
1725
- },
2539
+ }
1726
2540
 
2541
+ /**
2542
+ * @private
2543
+ * @description Applies properties to table cells.
2544
+ * @param {HTMLButtonElement} target The target element.
2545
+ */
1727
2546
  _submitProps(target) {
1728
2547
  try {
1729
- target.setAttribute('disabled', true);
2548
+ target.disabled = true;
1730
2549
 
1731
2550
  const isTable = this.controller_table.form.contains(this.controller_props.currentTarget);
1732
2551
  const targets = isTable ? [this._element] : this._selectedCells;
1733
- const tr = targets[0];
2552
+ const tr = /** @type {HTMLTableCellElement} */ (targets[0]);
1734
2553
  const trStyles = _w.getComputedStyle(tr);
1735
2554
  const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical } = this.propTargets;
1736
2555
 
@@ -1753,14 +2572,16 @@ Table.prototype = {
1753
2572
  top: [],
1754
2573
  right: [],
1755
2574
  bottom: [],
1756
- middle: []
2575
+ middle: [],
2576
+ all: null
1757
2577
  };
1758
2578
 
1759
2579
  if (!isTable) {
2580
+ const trRow = /** @type {HTMLTableRowElement} */ (tr.parentElement);
1760
2581
  // --- target cells roof
1761
2582
  let { rs, re, cs, ce } = this._ref || {
1762
- rs: tr.parentElement.rowIndex || 0,
1763
- re: tr.parentElement.rowIndex || 0,
2583
+ rs: trRow.rowIndex || 0,
2584
+ re: trRow.rowIndex || 0,
1764
2585
  cs: tr.cellIndex || 0,
1765
2586
  ce: tr.cellIndex || 0
1766
2587
  };
@@ -1770,17 +2591,17 @@ Table.prototype = {
1770
2591
  rs -= rs;
1771
2592
  ce -= cs;
1772
2593
  cs -= cs;
1773
- let prevRow = tr.parentNode;
2594
+ let prevRow = /** @type {HTMLElement} */ (trRow);
1774
2595
  for (let i = 0, cellCnt = 0, len = targets.length, e, es, rowIndex = 0, cellIndex, colspan, rowspan; i < len; i++, cellCnt++) {
1775
- e = targets[i];
2596
+ e = /** @type {HTMLTableCellElement} */ (targets[i]);
1776
2597
  colspan = e.colSpan;
1777
2598
  rowspan = e.rowSpan;
1778
2599
  cellIndex = e.cellIndex - cellStartIndex;
1779
2600
 
1780
- if (prevRow !== e.parentNode) {
2601
+ if (prevRow !== e.parentElement) {
1781
2602
  rowIndex++;
1782
2603
  cellCnt = 0;
1783
- prevRow = e.parentNode;
2604
+ prevRow = e.parentElement;
1784
2605
  }
1785
2606
 
1786
2607
  let c = 0;
@@ -1789,6 +2610,7 @@ Table.prototype = {
1789
2610
  c++;
1790
2611
  }
1791
2612
 
2613
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1792
2614
  try {
1793
2615
  if (rowspan > 1) {
1794
2616
  const rowspanNum = rowspan - 1;
@@ -1801,6 +2623,7 @@ Table.prototype = {
1801
2623
  } catch (err) {
1802
2624
  // ignore error
1803
2625
  }
2626
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1804
2627
 
1805
2628
  const isBottom = rowIndex + rowspan - 1 === re;
1806
2629
  if (rowIndex === rs) cells.top.push(e);
@@ -1874,91 +2697,101 @@ Table.prototype = {
1874
2697
  if (this._tdElement) {
1875
2698
  this._recallStyleSelectedCells();
1876
2699
  this.setCellInfo(this._tdElement, true);
1877
- domUtils.addClass(this._tdElement, 'se-selected-cell-focus');
2700
+ dom.utils.addClass(this._tdElement, 'se-selected-cell-focus');
1878
2701
  }
1879
2702
  } catch (err) {
1880
2703
  console.warn('[SUNEDITOR.plugins.table.setProps.error]', err);
1881
2704
  } finally {
1882
- target.removeAttribute('disabled');
2705
+ target.disabled = false;
1883
2706
  }
1884
- },
2707
+ }
1885
2708
 
2709
+ /**
2710
+ * @private
2711
+ * @description Sets font styles.
2712
+ * @param {CSSStyleDeclaration} styles The style object to modify.
2713
+ */
1886
2714
  _setFontStyle(styles) {
1887
2715
  const { font_bold, font_italic, font_strike, font_underline } = this.propTargets;
1888
- styles.fontWeight = domUtils.hasClass(font_bold, 'on') ? 'bold' : '';
1889
- styles.fontStyle = domUtils.hasClass(font_italic, 'on') ? 'italic' : '';
1890
- styles.textDecoration = ((domUtils.hasClass(font_strike, 'on') ? 'line-through ' : '') + (domUtils.hasClass(font_underline, 'on') ? 'underline' : '')).trim();
1891
- },
2716
+ styles.fontWeight = dom.utils.hasClass(font_bold, 'on') ? 'bold' : '';
2717
+ styles.fontStyle = dom.utils.hasClass(font_italic, 'on') ? 'italic' : '';
2718
+ styles.textDecoration = ((dom.utils.hasClass(font_strike, 'on') ? 'line-through ' : '') + (dom.utils.hasClass(font_underline, 'on') ? 'underline' : '')).trim();
2719
+ }
1892
2720
 
1893
2721
  /**
1894
2722
  * @private
1895
- * @description Set border format
1896
- * @param {Element[]} cells Target elements
1897
- * @param {"all"|"inside"|"horizon"|"vertical"|"outside"|"left"|"top"|"right"|"bottom"} borderKey Border style
1898
- * @param {number} s Border style
1899
- * @param {boolean} isTable table selected
2723
+ * @description Sets border format and styles.
2724
+ * @param {{left: Node[], top: Node[], right: Node[], bottom: Node[], all: Node[]}} cells The table cells categorized by border positions.
2725
+ * @param {string} borderKey Border style ("all"|"inside"|"horizon"|"vertical"|"outside"|"left"|"top"|"right"|"bottom")
2726
+ * @param {string} s The border style value.
1900
2727
  */
1901
2728
  _setBorderStyles(cells, borderKey, s) {
1902
2729
  const { left, top, right, bottom, all } = cells;
1903
2730
  switch (borderKey) {
1904
2731
  case 'inside':
1905
- if (cells.length === 1) return;
1906
- domUtils.setStyle(
2732
+ if (all.length === 1) return;
2733
+ dom.utils.setStyle(
1907
2734
  all.filter((c) => !bottom.includes(c)),
1908
2735
  BORDER_NS.b,
1909
2736
  s
1910
2737
  );
1911
- domUtils.setStyle(
2738
+ dom.utils.setStyle(
1912
2739
  all.filter((c) => !right.includes(c)),
1913
2740
  BORDER_NS.r,
1914
2741
  s
1915
2742
  );
1916
2743
  break;
1917
2744
  case 'horizon':
1918
- if (cells.length === 1) return;
1919
- domUtils.setStyle(
2745
+ if (all.length === 1) return;
2746
+ dom.utils.setStyle(
1920
2747
  all.filter((c) => !bottom.includes(c)),
1921
2748
  BORDER_NS.b,
1922
2749
  s
1923
2750
  );
1924
2751
  break;
1925
2752
  case 'vertical':
1926
- if (cells.length === 1) return;
1927
- domUtils.setStyle(
2753
+ if (all.length === 1) return;
2754
+ dom.utils.setStyle(
1928
2755
  all.filter((c) => !right.includes(c)),
1929
2756
  BORDER_NS.r,
1930
2757
  s
1931
2758
  );
1932
2759
  break;
1933
2760
  case 'outside':
1934
- domUtils.setStyle(left, BORDER_NS.l, s);
1935
- domUtils.setStyle(top, BORDER_NS.t, s);
1936
- domUtils.setStyle(right, BORDER_NS.r, s);
1937
- domUtils.setStyle(bottom, BORDER_NS.b, s);
2761
+ dom.utils.setStyle(left, BORDER_NS.l, s);
2762
+ dom.utils.setStyle(top, BORDER_NS.t, s);
2763
+ dom.utils.setStyle(right, BORDER_NS.r, s);
2764
+ dom.utils.setStyle(bottom, BORDER_NS.b, s);
1938
2765
  break;
1939
2766
  case 'left':
1940
- domUtils.setStyle(left, BORDER_NS.l, s);
2767
+ dom.utils.setStyle(left, BORDER_NS.l, s);
1941
2768
  break;
1942
2769
  case 'top':
1943
- domUtils.setStyle(top, BORDER_NS.t, s);
2770
+ dom.utils.setStyle(top, BORDER_NS.t, s);
1944
2771
  break;
1945
2772
  case 'right':
1946
- domUtils.setStyle(right, BORDER_NS.r, s);
2773
+ dom.utils.setStyle(right, BORDER_NS.r, s);
1947
2774
  break;
1948
2775
  case 'bottom':
1949
- domUtils.setStyle(bottom, BORDER_NS.b, s);
2776
+ dom.utils.setStyle(bottom, BORDER_NS.b, s);
1950
2777
  break;
1951
2778
  }
1952
- },
2779
+ }
1953
2780
 
2781
+ /**
2782
+ * @private
2783
+ * @description Selects multiple table cells and applies selection styles.
2784
+ * @param {Node} startCell The first cell in the selection.
2785
+ * @param {Node} endCell The last cell in the selection.
2786
+ */
1954
2787
  _setMultiCells(startCell, endCell) {
1955
2788
  const rows = this._selectedTable.rows;
1956
2789
  this._deleteStyleSelectedCells();
1957
2790
 
2791
+ dom.utils.addClass(startCell, 'se-selected-table-cell');
2792
+
1958
2793
  if (startCell === endCell) {
1959
2794
  if (!this._shift) return;
1960
- } else {
1961
- domUtils.addClass(startCell, 'se-selected-table-cell');
1962
2795
  }
1963
2796
 
1964
2797
  let findSelectedCell = true;
@@ -2034,7 +2867,7 @@ Table.prototype = {
2034
2867
  break;
2035
2868
  }
2036
2869
 
2037
- domUtils.addClass(cell, 'se-selected-table-cell');
2870
+ dom.utils.addClass(cell, 'se-selected-table-cell');
2038
2871
  }
2039
2872
 
2040
2873
  if (rs > 0) {
@@ -2054,8 +2887,12 @@ Table.prototype = {
2054
2887
  });
2055
2888
  rowSpanArr = [];
2056
2889
  }
2057
- },
2890
+ }
2058
2891
 
2892
+ /**
2893
+ * @private
2894
+ * @description Resets the table picker display.
2895
+ */
2059
2896
  _resetTablePicker() {
2060
2897
  if (!this.tableHighlight) return;
2061
2898
 
@@ -2067,10 +2904,14 @@ Table.prototype = {
2067
2904
  unHighlight.width = '10em';
2068
2905
  unHighlight.height = '10em';
2069
2906
 
2070
- domUtils.changeTxt(this.tableDisplay, '1 x 1');
2907
+ dom.utils.changeTxt(this.tableDisplay, '1 x 1');
2071
2908
  this.menu.dropdownOff();
2072
- },
2909
+ }
2073
2910
 
2911
+ /**
2912
+ * @private
2913
+ * @description Resets the alignment properties for table cells.
2914
+ */
2074
2915
  _resetPropsAlign() {
2075
2916
  const { cell_alignment } = this.propTargets;
2076
2917
  const left = cell_alignment.querySelector('[data-value="left"]');
@@ -2079,35 +2920,63 @@ Table.prototype = {
2079
2920
  const r_parent = right.parentElement;
2080
2921
  l_parent.appendChild(right);
2081
2922
  r_parent.appendChild(left);
2082
- },
2923
+ }
2083
2924
 
2925
+ /**
2926
+ * @private
2927
+ * @description Handles color selection from the color palette.
2928
+ * @param {Node} button The button triggering the color palette.
2929
+ * @param {string} type The type of color selection.
2930
+ * @param {HTMLInputElement} color Color text input element.
2931
+ */
2084
2932
  _onColorPalette(button, type, color) {
2085
2933
  if (this.controller_colorPicker.isOpen && type === this.sliderType) {
2086
2934
  this.controller_colorPicker.close();
2087
2935
  } else {
2088
2936
  this.sliderType = type;
2089
- domUtils.addClass(button, 'on');
2937
+ dom.utils.addClass(button, 'on');
2090
2938
  this.colorPicker.init(color?.value || '', button);
2091
2939
  this.controller_colorPicker.open(button, null, { isWWTarget: false, initMethod: null, addOffset: null });
2092
2940
  }
2093
- },
2941
+ }
2094
2942
 
2943
+ /**
2944
+ * @private
2945
+ * @description Closes table-related controllers.
2946
+ */
2095
2947
  _closeController() {
2096
- this.component.deselect();
2097
2948
  this.controller_table.close();
2098
2949
  this.controller_cell.close();
2099
- },
2950
+ }
2100
2951
 
2952
+ /**
2953
+ * @private
2954
+ * @description Closes table-related controllers and table figure
2955
+ */
2956
+ _closeTableSelectInfo() {
2957
+ this.component.deselect();
2958
+ this._closeController();
2959
+ }
2960
+
2961
+ /**
2962
+ * @private
2963
+ * @description Hides the resize line if it is visible.
2964
+ */
2101
2965
  __hideResizeLine() {
2102
2966
  if (this._resizeLine) {
2103
2967
  this._resizeLine.style.display = 'none';
2104
2968
  this._resizeLine = null;
2105
2969
  }
2106
- },
2970
+ }
2107
2971
 
2972
+ /**
2973
+ * @private
2974
+ * @description Removes global event listeners and resets resize-related properties.
2975
+ */
2108
2976
  __removeGlobalEvents() {
2977
+ this._toggleEditor(true);
2109
2978
  this._resizing = false;
2110
- this.editor.disableBackWrapper();
2979
+ this.ui.disableBackWrapper();
2111
2980
  this.__hideResizeLine();
2112
2981
  if (this._resizeLinePrev) {
2113
2982
  this._resizeLinePrev.style.display = 'none';
@@ -2117,380 +2986,635 @@ Table.prototype = {
2117
2986
  for (const k in globalEvents) {
2118
2987
  if (globalEvents[k]) globalEvents[k] = this.eventManager.removeGlobalEvent(globalEvents[k]);
2119
2988
  }
2120
- },
2121
-
2122
- constructor: Table
2123
- };
2989
+ }
2124
2990
 
2125
- function IsResizeEls(node) {
2126
- return /^(TD|TH|TR)$/i.test(node?.nodeName);
2127
- }
2991
+ /**
2992
+ * @description Clone a table element and map selected cells to the cloned table
2993
+ * @param {HTMLTableElement} table <table> element
2994
+ * @param {HTMLTableCellElement[]} selectedCells Selected cells array
2995
+ * @returns {{ cloneTable: HTMLTableElement, clonedSelectedCells: HTMLTableCellElement[] }}
2996
+ */
2997
+ #cloneTable(table, selectedCells) {
2998
+ /** @type {HTMLTableElement} */
2999
+ const cloneTable = dom.utils.clone(table, true);
3000
+
3001
+ const originalCells = Array.from(table.querySelectorAll('td, th'));
3002
+ const clonedCells = Array.from(cloneTable.querySelectorAll('td, th'));
3003
+
3004
+ const clonedSelectedCells = /** @type {HTMLTableCellElement[]} */ (
3005
+ selectedCells
3006
+ .map((cell) => {
3007
+ const index = originalCells.indexOf(cell);
3008
+ return index > -1 ? clonedCells[index] : null;
3009
+ })
3010
+ .filter((cell) => cell !== null)
3011
+ );
2128
3012
 
2129
- function CheckCellEdge(event, tableCell) {
2130
- const startX = event.clientX;
2131
- const startWidth = numbers.get(_w.getComputedStyle(tableCell).width, CELL_DECIMAL_END);
2132
- const rect = tableCell.getBoundingClientRect();
2133
- const offsetX = Math.round(startX - rect.left);
2134
- const isLeft = offsetX <= CELL_SELECT_MARGIN;
2135
- const is = isLeft || startWidth - offsetX <= CELL_SELECT_MARGIN;
3013
+ return {
3014
+ cloneTable,
3015
+ clonedSelectedCells
3016
+ };
3017
+ }
2136
3018
 
2137
- return {
2138
- is,
2139
- isLeft,
2140
- startX
2141
- };
2142
- }
3019
+ /**
3020
+ * @description Selects cells in a table, handling single and multi-cell selection, and managing shift key behavior for extended selection.
3021
+ * @param {HTMLTableCellElement} tdElement The target table cell (`<td>`) element that is being selected.
3022
+ * @param {boolean} shift A flag indicating whether the shift key is held down for multi-cell selection.
3023
+ * If `true`, the selection will extend to include adjacent cells, otherwise it selects only the provided cell.
3024
+ */
3025
+ #StyleSelectCells(tdElement, shift) {
3026
+ this.__s = shift;
3027
+ if (!this._shift && !this._ref) this.__removeGlobalEvents();
2143
3028
 
2144
- function CheckRowEdge(event, tableCell) {
2145
- const startY = event.clientY;
2146
- const startHeight = numbers.get(_w.getComputedStyle(tableCell).height, CELL_DECIMAL_END);
2147
- const rect = tableCell.getBoundingClientRect();
2148
- const is = Math.ceil(startHeight + rect.top - startY) <= ROW_SELECT_MARGIN;
3029
+ this._shift = shift;
3030
+ this._fixedCell = tdElement;
3031
+ if (!this._selectedCells?.length) this._selectedCells = [tdElement];
3032
+ this._fixedCellName = tdElement.nodeName;
3033
+ this._selectedTable = dom.query.getParentElement(tdElement, 'TABLE');
2149
3034
 
2150
- return {
2151
- is,
2152
- startY
2153
- };
2154
- }
3035
+ this._deleteStyleSelectedCells();
3036
+ dom.utils.addClass(tdElement, 'se-selected-cell-focus');
2155
3037
 
2156
- function OnSplitCells(direction) {
2157
- const vertical = direction === 'vertical';
2158
- const currentCell = this._tdElement;
2159
- const rows = this._trElements;
2160
- const currentRow = this._trElement;
2161
- const index = this._logical_cellIndex;
2162
- const rowIndex = this._rowIndex;
2163
- const newCell = CreateCells(currentCell.nodeName, 0, true);
2164
-
2165
- // vertical
2166
- if (vertical) {
2167
- const currentColSpan = currentCell.colSpan;
2168
- newCell.rowSpan = currentCell.rowSpan;
2169
-
2170
- // colspan > 1
2171
- if (currentColSpan > 1) {
2172
- newCell.colSpan = Math.floor(currentColSpan / 2);
2173
- currentCell.colSpan = currentColSpan - newCell.colSpan;
2174
- currentRow.insertBefore(newCell, currentCell.nextElementSibling);
3038
+ if (!shift) {
3039
+ this.__globalEvents.on = this.eventManager.addGlobalEvent('mousemove', this._bindMultiOn, false);
2175
3040
  } else {
2176
- // colspan - 1
2177
- let rowSpanArr = [];
2178
- let spanIndex = [];
3041
+ this.__globalEvents.shiftOff = this.eventManager.addGlobalEvent('keyup', this._bindShiftOff, false);
3042
+ this.__globalEvents.on = this.eventManager.addGlobalEvent('mousedown', this._bindMultiOn, false);
3043
+ }
2179
3044
 
2180
- for (let i = 0, len = this._rowCnt, cells, colSpan; i < len; i++) {
2181
- cells = rows[i].cells;
2182
- colSpan = 0;
2183
- for (let c = 0, cLen = cells.length, cell, cs, rs, logcalIndex; c < cLen; c++) {
2184
- cell = cells[c];
2185
- cs = cell.colSpan - 1;
2186
- rs = cell.rowSpan - 1;
2187
- logcalIndex = c + colSpan;
3045
+ this.__globalEvents.off = this.eventManager.addGlobalEvent('mouseup', this._bindMultiOff, false);
3046
+ this.__globalEvents.touchOff = this.eventManager.addGlobalEvent('touchmove', this._bindTouchOff, false);
3047
+ }
2188
3048
 
2189
- if (spanIndex.length > 0) {
2190
- for (let r = 0, arr; r < spanIndex.length; r++) {
2191
- arr = spanIndex[r];
2192
- if (arr.row > i) continue;
2193
- if (logcalIndex >= arr.index) {
2194
- colSpan += arr.cs;
2195
- logcalIndex += arr.cs;
2196
- arr.rs -= 1;
2197
- arr.row = i + 1;
2198
- if (arr.rs < 1) {
2199
- spanIndex.splice(r, 1);
2200
- r--;
2201
- }
2202
- } else if (c === cLen - 1) {
2203
- arr.rs -= 1;
2204
- arr.row = i + 1;
2205
- if (arr.rs < 1) {
2206
- spanIndex.splice(r, 1);
2207
- r--;
3049
+ /**
3050
+ * @description Splits a table cell either vertically or horizontally.
3051
+ * @param {"vertical"|"horizontal"} direction The direction to split the cell.
3052
+ */
3053
+ #OnSplitCells(direction) {
3054
+ const vertical = direction === 'vertical';
3055
+ const currentCell = this._tdElement;
3056
+ const rows = this._trElements;
3057
+ const currentRow = this._trElement;
3058
+ const index = this._logical_cellIndex;
3059
+ const rowIndex = this._rowIndex;
3060
+ const newCell = CreateCellsHTML(currentCell.nodeName);
3061
+
3062
+ // vertical
3063
+ if (vertical) {
3064
+ const currentColSpan = currentCell.colSpan;
3065
+ newCell.rowSpan = currentCell.rowSpan;
3066
+
3067
+ // colspan > 1
3068
+ if (currentColSpan > 1) {
3069
+ newCell.colSpan = Math.floor(currentColSpan / 2);
3070
+ currentCell.colSpan = currentColSpan - newCell.colSpan;
3071
+ currentRow.insertBefore(newCell, currentCell.nextElementSibling);
3072
+ } else {
3073
+ // colspan - 1
3074
+ let rowSpanArr = [];
3075
+ let spanIndex = [];
3076
+
3077
+ for (let i = 0, len = this._rowCnt, cells, colSpan; i < len; i++) {
3078
+ cells = rows[i].cells;
3079
+ colSpan = 0;
3080
+ for (let c = 0, cLen = cells.length, cell, cs, rs, logcalIndex; c < cLen; c++) {
3081
+ cell = cells[c];
3082
+ cs = cell.colSpan - 1;
3083
+ rs = cell.rowSpan - 1;
3084
+ logcalIndex = c + colSpan;
3085
+
3086
+ if (spanIndex.length > 0) {
3087
+ for (let r = 0, arr; r < spanIndex.length; r++) {
3088
+ arr = spanIndex[r];
3089
+ if (arr.row > i) continue;
3090
+ if (logcalIndex >= arr.index) {
3091
+ colSpan += arr.cs;
3092
+ logcalIndex += arr.cs;
3093
+ arr.rs -= 1;
3094
+ arr.row = i + 1;
3095
+ if (arr.rs < 1) {
3096
+ spanIndex.splice(r, 1);
3097
+ r--;
3098
+ }
3099
+ } else if (c === cLen - 1) {
3100
+ arr.rs -= 1;
3101
+ arr.row = i + 1;
3102
+ if (arr.rs < 1) {
3103
+ spanIndex.splice(r, 1);
3104
+ r--;
3105
+ }
2208
3106
  }
2209
3107
  }
2210
3108
  }
2211
- }
2212
3109
 
2213
- if (logcalIndex <= index && rs > 0) {
2214
- rowSpanArr.push({
2215
- index: logcalIndex,
2216
- cs: cs + 1,
2217
- rs: rs,
2218
- row: -1
2219
- });
2220
- }
3110
+ if (logcalIndex <= index && rs > 0) {
3111
+ rowSpanArr.push({
3112
+ index: logcalIndex,
3113
+ cs: cs + 1,
3114
+ rs: rs,
3115
+ row: -1
3116
+ });
3117
+ }
2221
3118
 
2222
- if (cell !== currentCell && logcalIndex <= index && logcalIndex + cs >= index + currentColSpan - 1) {
2223
- cell.colSpan += 1;
2224
- break;
2225
- }
3119
+ if (cell !== currentCell && logcalIndex <= index && logcalIndex + cs >= index + currentColSpan - 1) {
3120
+ cell.colSpan += 1;
3121
+ break;
3122
+ }
2226
3123
 
2227
- if (logcalIndex > index) break;
3124
+ if (logcalIndex > index) break;
2228
3125
 
2229
- colSpan += cs;
3126
+ colSpan += cs;
3127
+ }
3128
+
3129
+ spanIndex = spanIndex.concat(rowSpanArr).sort(function (a, b) {
3130
+ return a.index - b.index;
3131
+ });
3132
+ rowSpanArr = [];
2230
3133
  }
2231
3134
 
2232
- spanIndex = spanIndex.concat(rowSpanArr).sort(function (a, b) {
2233
- return a.index - b.index;
2234
- });
2235
- rowSpanArr = [];
3135
+ currentRow.insertBefore(newCell, currentCell.nextElementSibling);
2236
3136
  }
3137
+ } else {
3138
+ // horizontal
3139
+ const currentRowSpan = currentCell.rowSpan;
3140
+ newCell.colSpan = currentCell.colSpan;
2237
3141
 
2238
- currentRow.insertBefore(newCell, currentCell.nextElementSibling);
2239
- }
2240
- } else {
2241
- // horizontal
2242
- const currentRowSpan = currentCell.rowSpan;
2243
- newCell.colSpan = currentCell.colSpan;
3142
+ // rowspan > 1
3143
+ if (currentRowSpan > 1) {
3144
+ newCell.rowSpan = Math.floor(currentRowSpan / 2);
3145
+ const newRowSpan = currentRowSpan - newCell.rowSpan;
2244
3146
 
2245
- // rowspan > 1
2246
- if (currentRowSpan > 1) {
2247
- newCell.rowSpan = Math.floor(currentRowSpan / 2);
2248
- const newRowSpan = currentRowSpan - newCell.rowSpan;
3147
+ const rowSpanArr = [];
3148
+ const nextRowIndex = dom.utils.getArrayIndex(rows, currentRow) + newRowSpan;
3149
+
3150
+ for (let i = 0, cells, colSpan; i < nextRowIndex; i++) {
3151
+ cells = rows[i].cells;
3152
+ colSpan = 0;
3153
+ for (let c = 0, cLen = cells.length, cell, cs, logcalIndex; c < cLen; c++) {
3154
+ logcalIndex = c + colSpan;
3155
+ if (logcalIndex >= index) break;
3156
+
3157
+ cell = cells[c];
3158
+ cs = cell.rowSpan - 1;
3159
+ if (cs > 0 && cs + i >= nextRowIndex && logcalIndex < index) {
3160
+ rowSpanArr.push({
3161
+ index: logcalIndex,
3162
+ cs: cell.colSpan
3163
+ });
3164
+ }
3165
+ colSpan += cell.colSpan - 1;
3166
+ }
3167
+ }
2249
3168
 
2250
- const rowSpanArr = [];
2251
- const nextRowIndex = domUtils.getArrayIndex(rows, currentRow) + newRowSpan;
3169
+ const nextRow = rows[nextRowIndex];
3170
+ const nextCells = nextRow.cells;
3171
+ let rs = rowSpanArr.shift();
2252
3172
 
2253
- for (let i = 0, cells, colSpan; i < nextRowIndex; i++) {
2254
- cells = rows[i].cells;
2255
- colSpan = 0;
2256
- for (let c = 0, cLen = cells.length, cell, cs, logcalIndex; c < cLen; c++) {
3173
+ for (let c = 0, cLen = nextCells.length, colSpan = 0, cell, cs, logcalIndex, insertIndex; c < cLen; c++) {
2257
3174
  logcalIndex = c + colSpan;
2258
- if (logcalIndex >= index) break;
3175
+ cell = nextCells[c];
3176
+ cs = cell.colSpan - 1;
3177
+ insertIndex = logcalIndex + cs + 1;
2259
3178
 
2260
- cell = cells[c];
2261
- cs = cell.rowSpan - 1;
2262
- if (cs > 0 && cs + i >= nextRowIndex && logcalIndex < index) {
2263
- rowSpanArr.push({
2264
- index: logcalIndex,
2265
- cs: cell.colSpan
2266
- });
3179
+ if (rs && insertIndex >= rs.index) {
3180
+ colSpan += rs.cs;
3181
+ insertIndex += rs.cs;
3182
+ rs = rowSpanArr.shift();
2267
3183
  }
2268
- colSpan += cell.colSpan - 1;
2269
- }
2270
- }
2271
3184
 
2272
- const nextRow = rows[nextRowIndex];
2273
- const nextCells = nextRow.cells;
2274
- let rs = rowSpanArr.shift();
2275
-
2276
- for (let c = 0, cLen = nextCells.length, colSpan = 0, cell, cs, logcalIndex, insertIndex; c < cLen; c++) {
2277
- logcalIndex = c + colSpan;
2278
- cell = nextCells[c];
2279
- cs = cell.colSpan - 1;
2280
- insertIndex = logcalIndex + cs + 1;
3185
+ if (insertIndex >= index || c === cLen - 1) {
3186
+ nextRow.insertBefore(newCell, cell.nextElementSibling);
3187
+ break;
3188
+ }
2281
3189
 
2282
- if (rs && insertIndex >= rs.index) {
2283
- colSpan += rs.cs;
2284
- insertIndex += rs.cs;
2285
- rs = rowSpanArr.shift();
3190
+ colSpan += cs;
2286
3191
  }
2287
3192
 
2288
- if (insertIndex >= index || c === cLen - 1) {
2289
- nextRow.insertBefore(newCell, cell.nextElementSibling);
2290
- break;
3193
+ currentCell.rowSpan = newRowSpan;
3194
+ } else {
3195
+ // rowspan - 1
3196
+ newCell.rowSpan = currentCell.rowSpan;
3197
+ const newRow = dom.utils.createElement('TR');
3198
+ newRow.appendChild(newCell);
3199
+
3200
+ for (let i = 0, cells; i < rowIndex; i++) {
3201
+ cells = rows[i].cells;
3202
+ if (cells.length === 0) return;
3203
+
3204
+ for (let c = 0, cLen = cells.length; c < cLen; c++) {
3205
+ if (i + cells[c].rowSpan - 1 >= rowIndex) {
3206
+ cells[c].rowSpan += 1;
3207
+ }
3208
+ }
2291
3209
  }
2292
3210
 
2293
- colSpan += cs;
2294
- }
2295
-
2296
- currentCell.rowSpan = newRowSpan;
2297
- } else {
2298
- // rowspan - 1
2299
- newCell.rowSpan = currentCell.rowSpan;
2300
- const newRow = domUtils.createElement('TR');
2301
- newRow.appendChild(newCell);
2302
-
2303
- for (let i = 0, cells; i < rowIndex; i++) {
2304
- cells = rows[i].cells;
2305
- if (cells.length === 0) return;
3211
+ const physicalIndex = this._physical_cellIndex;
3212
+ const cells = currentRow.cells;
2306
3213
 
2307
3214
  for (let c = 0, cLen = cells.length; c < cLen; c++) {
2308
- if (i + cells[c].rowSpan - 1 >= rowIndex) {
2309
- cells[c].rowSpan += 1;
2310
- }
3215
+ if (c === physicalIndex) continue;
3216
+ cells[c].rowSpan += 1;
2311
3217
  }
3218
+
3219
+ currentRow.parentNode.insertBefore(newRow, currentRow.nextElementSibling);
2312
3220
  }
3221
+ }
2313
3222
 
2314
- const physicalIndex = this._physical_cellIndex;
2315
- const cells = currentRow.cells;
3223
+ this.selectMenu_split.close();
3224
+ this.#focusEdge(currentCell);
2316
3225
 
2317
- for (let c = 0, cLen = cells.length; c < cLen; c++) {
2318
- if (c === physicalIndex) continue;
2319
- cells[c].rowSpan += 1;
2320
- }
3226
+ this._deleteStyleSelectedCells();
3227
+ this.history.push(false);
2321
3228
 
2322
- currentRow.parentNode.insertBefore(newRow, currentRow.nextElementSibling);
2323
- }
3229
+ this._setController(currentCell);
3230
+ this._selectedCell = this._fixedCell = currentCell;
3231
+ if (!this._selectedCells?.length) this._selectedCells = [currentCell];
2324
3232
  }
2325
3233
 
2326
- this.selectMenu_split.close();
2327
- this.editor.focusEdge(currentCell);
3234
+ /**
3235
+ * @description Handles column operations such as insert and delete.
3236
+ * @param {"insert-left"|"insert-right"|"delete"} command The column operation to perform.
3237
+ */
3238
+ #OnColumnEdit(command) {
3239
+ switch (command) {
3240
+ case 'insert-left':
3241
+ this.editTable('cell', 'left');
3242
+ break;
3243
+ case 'insert-right':
3244
+ this.editTable('cell', 'right');
3245
+ break;
3246
+ case 'delete':
3247
+ this.editTable('cell', null);
3248
+ }
2328
3249
 
2329
- this._deleteStyleSelectedCells();
2330
- this.history.push(false);
3250
+ this._historyPush();
3251
+ }
2331
3252
 
2332
- this.setController(currentCell);
2333
- this._selectedCell = this._fixedCell = currentCell;
2334
- }
3253
+ /**
3254
+ * @description Handles row operations such as insert and delete.
3255
+ * @param {"insert-above"|"insert-below"|"delete"} command The row operation to perform.
3256
+ */
3257
+ #OnRowEdit(command) {
3258
+ switch (command) {
3259
+ case 'insert-above':
3260
+ this.editTable('row', 'up');
3261
+ break;
3262
+ case 'insert-below':
3263
+ this.editTable('row', 'down');
3264
+ break;
3265
+ case 'delete':
3266
+ this.editTable('row', null);
3267
+ }
2335
3268
 
2336
- function OnColumnEdit(command) {
2337
- switch (command) {
2338
- case 'insert-left':
2339
- this.editTable('cell', 'left');
2340
- break;
2341
- case 'insert-right':
2342
- this.editTable('cell', 'right');
2343
- break;
2344
- case 'delete':
2345
- this.editTable('cell', null);
3269
+ this._historyPush();
2346
3270
  }
2347
3271
 
2348
- this._historyPush();
2349
- }
3272
+ /**
3273
+ * @description Handles mouse movement within the table picker.
3274
+ * @param {MouseEvent} e The mouse event.
3275
+ */
3276
+ #OnMouseMoveTablePicker(e) {
3277
+ e.stopPropagation();
2350
3278
 
2351
- function OnRowEdit(command) {
2352
- switch (command) {
2353
- case 'insert-above':
2354
- this.editTable('row', 'up');
2355
- break;
2356
- case 'insert-below':
2357
- this.editTable('row', 'down');
2358
- break;
2359
- case 'delete':
2360
- this.editTable('row', null);
2361
- }
3279
+ let x = Math.ceil(e.offsetX / 18);
3280
+ let y = Math.ceil(e.offsetY / 18);
3281
+ x = x < 1 ? 1 : x;
3282
+ y = y < 1 ? 1 : y;
2362
3283
 
2363
- this._historyPush();
2364
- }
3284
+ if (this.options.get('_rtl')) {
3285
+ this.tableHighlight.style.left = x * 18 - 13 + 'px';
3286
+ x = 11 - x;
3287
+ }
2365
3288
 
2366
- function OnMouseMoveTablePicker(e) {
2367
- e.stopPropagation();
3289
+ this.tableHighlight.style.width = x + 'em';
3290
+ this.tableHighlight.style.height = y + 'em';
2368
3291
 
2369
- let x = Math.ceil(e.offsetX / 18);
2370
- let y = Math.ceil(e.offsetY / 18);
2371
- x = x < 1 ? 1 : x;
2372
- y = y < 1 ? 1 : y;
3292
+ const x_u = x < 5 ? 5 : x > 8 ? 10 : x + 2;
3293
+ const y_u = y < 5 ? 5 : y > 8 ? 10 : y + 2;
3294
+ this.tableUnHighlight.style.width = x_u + 'em';
3295
+ this.tableUnHighlight.style.height = y_u + 'em';
2373
3296
 
2374
- if (this.options.get('_rtl')) {
2375
- this.tableHighlight.style.left = x * 18 - 13 + 'px';
2376
- x = 11 - x;
3297
+ dom.utils.changeTxt(this.tableDisplay, x + ' x ' + y);
3298
+ this._tableXY = [x, y];
2377
3299
  }
2378
3300
 
2379
- this.tableHighlight.style.width = x + 'em';
2380
- this.tableHighlight.style.height = y + 'em';
2381
-
2382
- const x_u = x < 5 ? 5 : x > 8 ? 10 : x + 2;
2383
- const y_u = y < 5 ? 5 : y > 8 ? 10 : y + 2;
2384
- this.tableUnHighlight.style.width = x_u + 'em';
2385
- this.tableUnHighlight.style.height = y_u + 'em';
3301
+ /**
3302
+ * @description Executes the selected action when the table picker is clicked.
3303
+ */
3304
+ #OnClickTablePicker() {
3305
+ this.action();
3306
+ }
2386
3307
 
2387
- domUtils.changeTxt(this.tableDisplay, x + ' x ' + y);
2388
- this._tableXY = [x, y];
2389
- }
3308
+ /**
3309
+ * @description Handles multi-selection of table cells.
3310
+ * @param {MouseEvent} e The mouse event.
3311
+ */
3312
+ #OnCellMultiSelect(e) {
3313
+ this.editor._preventBlur = true;
3314
+ const target = /** @type {HTMLTableCellElement} */ (dom.query.getParentElement(dom.query.getEventTarget(e), dom.check.isTableCell));
2390
3315
 
2391
- function OnClickTablePicker() {
2392
- this.action();
2393
- }
3316
+ if (this._shift) {
3317
+ if (target === this._fixedCell) {
3318
+ this._shift = false;
3319
+ this._deleteStyleSelectedCells();
3320
+ this._toggleEditor(true);
3321
+ this.__removeGlobalEvents();
3322
+ return;
3323
+ } else {
3324
+ this._toggleEditor(false);
3325
+ }
3326
+ } else if (!this._ref) {
3327
+ if (target === this._fixedCell) return;
3328
+ else this._toggleEditor(false);
3329
+ }
2394
3330
 
2395
- function CreateCells(nodeName, cnt, returnElement) {
2396
- nodeName = nodeName.toLowerCase();
3331
+ if (!target || target === this._selectedCell || this._fixedCellName !== target.nodeName || this._selectedTable !== dom.query.getParentElement(target, 'TABLE')) {
3332
+ return;
3333
+ }
2397
3334
 
2398
- if (!returnElement) {
2399
- return `<${nodeName}><div><br></div></${nodeName}>`.repeat(cnt);
2400
- } else {
2401
- return domUtils.createElement(nodeName, null, '<div><br></div>');
3335
+ this._setMultiCells(this._fixedCell, (this._selectedCell = target));
2402
3336
  }
2403
- }
2404
3337
 
2405
- function OnCellMultiSelect(e) {
2406
- this.editor._antiBlur = true;
2407
- const target = domUtils.getParentElement(e.target, domUtils.isTableCell);
3338
+ /**
3339
+ * @description Stops multi-selection of table cells.
3340
+ * @param {MouseEvent} e The mouse event.
3341
+ */
3342
+ #OffCellMultiSelect(e) {
3343
+ e.stopPropagation();
2408
3344
 
2409
- if (this._shift) {
2410
- if (target === this._fixedCell) {
2411
- this._shift = false;
2412
- this._deleteStyleSelectedCells();
3345
+ if (!this._shift) {
2413
3346
  this._toggleEditor(true);
2414
3347
  this.__removeGlobalEvents();
2415
- return;
3348
+ } else if (this.__globalEvents.touchOff) {
3349
+ this.__globalEvents.touchOff = this.eventManager.removeGlobalEvent(this.__globalEvents.touchOff);
3350
+ }
3351
+
3352
+ if (!this._fixedCell || !this._selectedTable) return;
3353
+
3354
+ this._setMergeSplitButton();
3355
+ this._selectedCells = Array.from(this._selectedTable.querySelectorAll('.se-selected-table-cell'));
3356
+
3357
+ if (this._shift) return;
3358
+
3359
+ if (this._fixedCell && this._selectedCell) {
3360
+ this.#focusEdge(this._fixedCell);
3361
+ if (this._fixedCell === this._selectedCell) {
3362
+ dom.utils.removeClass(this._fixedCell, 'se-selected-table-cell');
3363
+ }
3364
+ }
3365
+
3366
+ const displayCell = this._selectedCells?.length > 0 ? this._selectedCell : this._fixedCell;
3367
+ this._setController(displayCell);
3368
+ }
3369
+
3370
+ /**
3371
+ * @description Handles the removal of shift-based selection.
3372
+ */
3373
+ #OffCellShift() {
3374
+ if (!this._ref) {
3375
+ this._closeController();
2416
3376
  } else {
2417
- this._toggleEditor(false);
3377
+ this.__removeGlobalEvents();
3378
+ this._toggleEditor(true);
3379
+
3380
+ this.#focusEdge(this._fixedCell);
3381
+
3382
+ const displayCell = this._selectedCells?.length > 0 ? this._selectedCell : this._fixedCell;
3383
+ this._setController(displayCell);
2418
3384
  }
2419
- } else if (!this._ref) {
2420
- if (target === this._fixedCell) return;
2421
- else this._toggleEditor(false);
2422
3385
  }
2423
3386
 
2424
- if (!target || target === this._selectedCell || this._fixedCellName !== target.nodeName || this._selectedTable !== domUtils.getParentElement(target, 'TABLE')) {
2425
- return;
3387
+ /**
3388
+ * @description Handles the removal of touch-based selection.
3389
+ */
3390
+ #OffCellTouch() {
3391
+ this.close();
2426
3392
  }
2427
3393
 
2428
- this._selectedCell = target;
2429
- this._setMultiCells(this._fixedCell, target);
3394
+ /**
3395
+ * @description Focus cell
3396
+ * @param {HTMLElement} cell Target node
3397
+ */
3398
+ #focusEdge(cell) {
3399
+ if (!env.isMobile) this.editor.focusEdge(cell);
3400
+ }
2430
3401
  }
2431
3402
 
2432
- function OffCellMultiSelect(e) {
2433
- e.stopPropagation();
3403
+ /**
3404
+ * @private
3405
+ * @description Checks if the given node is a resizable table element.
3406
+ * @param {Node} node The DOM node to check.
3407
+ * @returns {boolean} True if the node is a table-related resizable element.
3408
+ */
3409
+ function IsResizeEls(node) {
3410
+ return /^(TD|TH|TR)$/i.test(node?.nodeName);
3411
+ }
2434
3412
 
2435
- if (!this._shift) {
2436
- this.__removeGlobalEvents();
2437
- this._toggleEditor(true);
2438
- } else if (this.__globalEvents.touchOff) {
2439
- this.__globalEvents.touchOff = this.eventManager.removeGlobalEvent(this.__globalEvents.touchOff);
2440
- }
3413
+ /**
3414
+ * @private
3415
+ * @description Checks if a table cell is at its edge based on the mouse event.
3416
+ * @param {MouseEvent} event The mouse event.
3417
+ * @param {Element} tableCell The table cell to check.
3418
+ * @returns {Object} An object containing edge detection details.
3419
+ */
3420
+ function CheckCellEdge(event, tableCell) {
3421
+ const startX = event.clientX;
3422
+ const startWidth = numbers.get(_w.getComputedStyle(tableCell).width, CELL_DECIMAL_END);
3423
+ const rect = tableCell.getBoundingClientRect();
3424
+ const offsetX = Math.round(startX - rect.left);
3425
+ const isLeft = offsetX <= CELL_SELECT_MARGIN;
3426
+ const is = isLeft || startWidth - offsetX <= CELL_SELECT_MARGIN;
2441
3427
 
2442
- if (!this._fixedCell || !this._selectedTable) return;
3428
+ return {
3429
+ is,
3430
+ isLeft,
3431
+ startX
3432
+ };
3433
+ }
2443
3434
 
2444
- this.setMergeSplitButton(this._fixedCell, this._selectedCell);
2445
- this._selectedCells = Array.from(this._selectedTable.querySelectorAll('.se-selected-table-cell'));
3435
+ /**
3436
+ * @private
3437
+ * @description Checks if a row is at its edge based on the mouse event.
3438
+ * @param {MouseEvent} event The mouse event.
3439
+ * @param {Element} tableCell The table row cell to check.
3440
+ * @returns {Object} An object containing row edge detection details.
3441
+ */
3442
+ function CheckRowEdge(event, tableCell) {
3443
+ const startY = event.clientY;
3444
+ const startHeight = numbers.get(_w.getComputedStyle(tableCell).height, CELL_DECIMAL_END);
3445
+ const rect = tableCell.getBoundingClientRect();
3446
+ const is = Math.ceil(startHeight + rect.top - startY) <= ROW_SELECT_MARGIN;
2446
3447
 
2447
- const focusCell = this._selectedCells?.length > 0 ? this._selectedCell : this._fixedCell;
2448
- this.setController(focusCell);
3448
+ return {
3449
+ is,
3450
+ startY
3451
+ };
2449
3452
  }
2450
3453
 
2451
- function OffCellShift() {
2452
- if (!this._ref) this._closeController();
3454
+ /**
3455
+ * @private
3456
+ * @description Creates table cells as elements strings.
3457
+ * @param {string} nodeName The tag name of the cell (TD or TH).
3458
+ * @param {number} cnt The number of cells to create.
3459
+ * @returns {string} The created cells.
3460
+ */
3461
+ function CreateCellsString(nodeName, cnt) {
3462
+ nodeName = nodeName.toLowerCase();
3463
+ return `<${nodeName}><div><br></div></${nodeName}>`.repeat(cnt);
2453
3464
  }
2454
3465
 
2455
- function OffCellTouch() {
2456
- this.close();
3466
+ /**
3467
+ * @private
3468
+ * @description Creates table cells as element HTML.
3469
+ * @param {string} nodeName The tag name of the cell (TD or TH).
3470
+ * @returns {HTMLTableCellElement} The created cells.
3471
+ */
3472
+ function CreateCellsHTML(nodeName) {
3473
+ nodeName = nodeName.toLowerCase();
3474
+ return /** @type {HTMLTableCellElement} */ (dom.utils.createElement(nodeName, null, '<div><br></div>'));
2457
3475
  }
2458
3476
 
3477
+ /**
3478
+ * @private
3479
+ * @description Gets the maximum number of columns in a table.
3480
+ * @param {HTMLTableElement} table The table element.
3481
+ * @returns {number} The maximum number of columns in the table.
3482
+ */
2459
3483
  function GetMaxColumns(table) {
3484
+ const rows = table.rows;
2460
3485
  let maxColumns = 0;
2461
3486
 
2462
- for (const row of table.rows) {
3487
+ for (let i = 0, len = rows.length; i < len; i++) {
3488
+ const cells = rows[i].cells;
2463
3489
  let columnCount = 0;
2464
- for (const cell of row.cells) {
2465
- columnCount += cell.colSpan;
3490
+
3491
+ for (let j = 0, jLen = cells.length; j < jLen; j++) {
3492
+ columnCount += cells[j].colSpan;
2466
3493
  }
3494
+
2467
3495
  maxColumns = Math.max(maxColumns, columnCount);
2468
3496
  }
2469
3497
 
2470
3498
  return maxColumns;
2471
3499
  }
2472
3500
 
3501
+ /**
3502
+ * @private
3503
+ * @description Handles border style changes in table properties.
3504
+ * @param {string} command The border style command.
3505
+ */
2473
3506
  function OnPropsBorderEdit(command) {
2474
3507
  this.propTargets.border_style.textContent = command;
2475
3508
  this._disableBorderProps(command === BORDER_LIST[0]);
2476
3509
  this.selectMenu_props_border.close();
2477
3510
  }
2478
3511
 
3512
+ /**
3513
+ * @private
3514
+ * @description Handles border format changes in table properties.
3515
+ * @param {string} defaultCommand The default border format command.
3516
+ * @param {string} command The new border format command.
3517
+ */
2479
3518
  function OnPropsBorderFormatEdit(defaultCommand, command) {
2480
3519
  const { border_format } = this.propTargets;
2481
3520
 
2482
3521
  border_format.setAttribute('se-border-format', command);
2483
3522
  border_format.firstElementChild.innerHTML = this.icons[BORDER_FORMATS[command]];
2484
- if (command !== defaultCommand) domUtils.addClass(border_format, 'active');
2485
- else domUtils.removeClass(border_format, 'active');
3523
+ if (command !== defaultCommand) dom.utils.addClass(border_format, 'active');
3524
+ else dom.utils.removeClass(border_format, 'active');
2486
3525
 
2487
3526
  this.selectMenu_props_border_format.close();
2488
3527
  this.selectMenu_props_border_format_oneCell.close();
2489
3528
  }
2490
3529
 
3530
+ /**
3531
+ * @description Creates the table properties controller.
3532
+ * @param {ClipboardEvent} e - Event object
3533
+ * @param {HTMLElement} container - The container element
3534
+ * @param {NodeListOf<HTMLTableCellElement>} selectedCells - The selected table cells
3535
+ */
3536
+ function SetClipboardSelectedTableCells(e, container, selectedCells) {
3537
+ e.preventDefault();
3538
+ e.stopPropagation();
3539
+
3540
+ const originalTable = selectedCells[0].closest('table');
3541
+ const tempTable = originalTable.cloneNode(false);
3542
+ const tbody = dom.utils.createElement('tbody');
3543
+ tempTable.appendChild(tbody);
3544
+
3545
+ const cellPositions = new Map();
3546
+ selectedCells.forEach((cell) => {
3547
+ cellPositions.set(cell, true);
3548
+ });
3549
+
3550
+ const rows = originalTable.rows;
3551
+ const rowCount = rows.length;
3552
+ const colCount = Array.from(rows[0].cells).reduce((sum, cell) => sum + (cell.colSpan || 1), 0);
3553
+ const matrix = Array.from({ length: rowCount }, () => Array(colCount).fill(null));
3554
+
3555
+ // build matrix
3556
+ for (let r = 0, realRow = 0; r < rowCount; r++, realRow++) {
3557
+ const cells = rows[r].cells;
3558
+ for (let c = 0, realCol = 0, cLen = cells.length; c < cLen; c++) {
3559
+ while (matrix[realRow][realCol]) realCol++;
3560
+ const cell = cells[c];
3561
+ const rowspan = cell.rowSpan || 1;
3562
+ const colspan = cell.colSpan || 1;
3563
+ for (let i = 0; i < rowspan; i++) {
3564
+ for (let j = 0; j < colspan; j++) {
3565
+ matrix[realRow + i][realCol + j] = cell;
3566
+ }
3567
+ }
3568
+ realCol += colspan;
3569
+ }
3570
+ }
3571
+
3572
+ // construct new table
3573
+ for (let r = 0; r < rowCount; r++) {
3574
+ let newRow;
3575
+ for (let c = 0; c < colCount; c++) {
3576
+ const cell = matrix[r][c];
3577
+ if (!cell || !cellPositions.has(cell)) continue;
3578
+
3579
+ if (!newRow) {
3580
+ newRow = dom.utils.createElement('tr');
3581
+ tbody.appendChild(newRow);
3582
+ }
3583
+
3584
+ if (newRow.lastChild && matrix[r][c - 1] === cell) continue;
3585
+ if (r > 0 && matrix[r - 1][c] === cell) continue;
3586
+
3587
+ const clonedCell = cell.cloneNode(true);
3588
+
3589
+ // recalculate rowspan and colspan
3590
+ let rowspan = 1;
3591
+ let colspan = 1;
3592
+ while (r + rowspan < rowCount && matrix[r + rowspan][c] === cell) rowspan++;
3593
+ while (c + colspan < colCount && matrix[r][c + colspan] === cell) colspan++;
3594
+
3595
+ if (rowspan > 1) clonedCell.rowSpan = rowspan;
3596
+ if (colspan > 1) clonedCell.colSpan = colspan;
3597
+
3598
+ newRow.appendChild(clonedCell);
3599
+ }
3600
+ }
3601
+
3602
+ const figure = dom.utils.createElement('figure');
3603
+ figure.className = container.className;
3604
+ figure.appendChild(tempTable);
3605
+
3606
+ const htmlContent = `<html><body><!--StartFragment-->${figure.outerHTML}<!--EndFragment--></body></html>`;
3607
+ e.clipboardData.setData('text/html', htmlContent);
3608
+ }
3609
+
3610
+ function _CellFormZIndex(value) {
3611
+ this.controller_cell.bringToTop(value);
3612
+ }
3613
+
3614
+ /** --------------------- HTML Create --------------------- */
2491
3615
  // init element
2492
3616
  function CreateSplitMenu(lang) {
2493
- const menus = domUtils.createElement(
3617
+ const menus = dom.utils.createElement(
2494
3618
  'DIV',
2495
3619
  null,
2496
3620
  /*html*/ `
@@ -2506,7 +3630,7 @@ function CreateSplitMenu(lang) {
2506
3630
  }
2507
3631
 
2508
3632
  function CreateColumnMenu(lang, icons) {
2509
- const menus = domUtils.createElement(
3633
+ const menus = dom.utils.createElement(
2510
3634
  'DIV',
2511
3635
  null,
2512
3636
  /*html*/ `
@@ -2525,7 +3649,7 @@ function CreateColumnMenu(lang, icons) {
2525
3649
  }
2526
3650
 
2527
3651
  function CreateRowMenu(lang, icons) {
2528
- const menus = domUtils.createElement(
3652
+ const menus = dom.utils.createElement(
2529
3653
  'DIV',
2530
3654
  null,
2531
3655
  /*html*/ `
@@ -2554,7 +3678,7 @@ function CreateBorderMenu() {
2554
3678
  </div>`;
2555
3679
  }
2556
3680
 
2557
- const menus = domUtils.createElement('DIV', null, html);
3681
+ const menus = dom.utils.createElement('DIV', null, html);
2558
3682
  return { items: BORDER_LIST, menus: menus.querySelectorAll('div') };
2559
3683
  }
2560
3684
 
@@ -2575,7 +3699,7 @@ function CreateBorderFormatMenu(langs, icons, indideFormats) {
2575
3699
  </button>`;
2576
3700
  }
2577
3701
 
2578
- const menus = domUtils.createElement('DIV', null, html);
3702
+ const menus = dom.utils.createElement('DIV', null, html);
2579
3703
  return { items, menus: menus.querySelectorAll('button') };
2580
3704
  }
2581
3705
 
@@ -2588,7 +3712,7 @@ function CreateHTML() {
2588
3712
  </div>
2589
3713
  <div class="se-table-size-display">1 x 1</div>`;
2590
3714
 
2591
- return domUtils.createElement('DIV', { class: 'se-dropdown se-selector-table' }, html);
3715
+ return dom.utils.createElement('DIV', { class: 'se-dropdown se-selector-table' }, html);
2592
3716
  }
2593
3717
 
2594
3718
  function CreateHTML_controller_table({ lang, icons }) {
@@ -2625,6 +3749,12 @@ function CreateHTML_controller_table({ lang, icons }) {
2625
3749
  <span class="se-tooltip-text">${lang.minSize}</span>
2626
3750
  </span>
2627
3751
  </button>
3752
+ <button type="button" data-command="copy" class="se-btn se-tooltip">
3753
+ ${icons.copy}
3754
+ <span class="se-tooltip-inner">
3755
+ <span class="se-tooltip-text">${lang.copy}</span>
3756
+ </span>
3757
+ </button>
2628
3758
  <button type="button" data-command="remove" class="se-btn se-tooltip">
2629
3759
  ${icons.delete}
2630
3760
  <span class="se-tooltip-inner">
@@ -2633,9 +3763,13 @@ function CreateHTML_controller_table({ lang, icons }) {
2633
3763
  </button>
2634
3764
  </div>`;
2635
3765
 
2636
- return domUtils.createElement('DIV', { class: 'se-controller se-controller-table' }, html);
3766
+ return dom.utils.createElement('DIV', { class: 'se-controller se-controller-table' }, html);
2637
3767
  }
2638
3768
 
3769
+ /**
3770
+ * @param {__se__EditorCore} editor
3771
+ * @returns {{ html: HTMLElement, splitButton: HTMLButtonElement, columnButton: HTMLButtonElement, rowButton: HTMLButtonElement, mergeButton: HTMLButtonElement, unmergeButton: HTMLButtonElement }}
3772
+ */
2639
3773
  function CreateHTML_controller_cell({ lang, icons }, cellControllerTop) {
2640
3774
  const html = /*html*/ `
2641
3775
  <div class="se-arrow se-arrow-${cellControllerTop ? 'down se-visible-hidden' : 'up'}"></div>
@@ -2669,12 +3803,50 @@ function CreateHTML_controller_cell({ lang, icons }, cellControllerTop) {
2669
3803
  <span class="se-tooltip-inner">
2670
3804
  <span class="se-tooltip-text">${lang.splitCells}</span>
2671
3805
  </span>
3806
+ </button>
3807
+ <button type="button" data-command="unmerge" class="se-btn se-tooltip">
3808
+ ${icons.unmerge_cell}
3809
+ <span class="se-tooltip-inner">
3810
+ <span class="se-tooltip-text">${lang.unmergeCells}</span>
3811
+ </span>
2672
3812
  </button>
2673
3813
  </div>`;
2674
3814
 
2675
- return domUtils.createElement('DIV', { class: 'se-controller se-controller-table-cell' }, html);
3815
+ const content = dom.utils.createElement('DIV', { class: 'se-controller se-controller-table-cell' }, html);
3816
+
3817
+ return {
3818
+ html: content,
3819
+ splitButton: content.querySelector('[data-command="onsplit"]'),
3820
+ columnButton: content.querySelector('[data-command="oncolumn"]'),
3821
+ rowButton: content.querySelector('[data-command="onrow"]'),
3822
+ mergeButton: content.querySelector('[data-command="merge"]'),
3823
+ unmergeButton: content.querySelector('[data-command="unmerge"]')
3824
+ };
2676
3825
  }
2677
3826
 
3827
+ /**
3828
+ * @typedef {Object} TableCtrlProps
3829
+ * @property {HTMLElement} html
3830
+ * @property {HTMLElement} controller_props_title
3831
+ * @property {HTMLButtonElement} borderButton
3832
+ * @property {HTMLButtonElement} borderFormatButton
3833
+ * @property {HTMLElement} cell_alignment
3834
+ * @property {HTMLElement} cell_alignment_vertical
3835
+ * @property {HTMLElement} cell_alignment_table_text
3836
+ * @property {HTMLButtonElement} border_style
3837
+ * @property {HTMLInputElement} border_color
3838
+ * @property {HTMLInputElement} border_width
3839
+ * @property {HTMLInputElement} back_color
3840
+ * @property {HTMLInputElement} font_color
3841
+ * @property {HTMLButtonElement} palette_border_button
3842
+ * @property {HTMLButtonElement} font_bold
3843
+ * @property {HTMLButtonElement} font_underline
3844
+ * @property {HTMLButtonElement} font_italic
3845
+ * @property {HTMLButtonElement} font_strike
3846
+ *
3847
+ * @param {__se__EditorCore} editor - Editor instance
3848
+ * @returns {TableCtrlProps}
3849
+ */
2678
3850
  function CreateHTML_controller_properties({ lang, icons, options }) {
2679
3851
  const alignItems = options.get('_rtl') ? ['right', 'center', 'left', 'justify'] : ['left', 'center', 'right', 'justify'];
2680
3852
  let alignHtml = '';
@@ -2682,14 +3854,12 @@ function CreateHTML_controller_properties({ lang, icons, options }) {
2682
3854
  item = alignItems[i];
2683
3855
  text = lang['align' + item.charAt(0).toUpperCase() + item.slice(1)];
2684
3856
  alignHtml += /*html*/ `
2685
- <li>
2686
- <button type="button" class="se-btn se-btn-list se-tooltip" data-command="props_align" data-value="${item}" title="${text}" aria-label="${text}">
2687
- ${icons['align_' + item]}
2688
- <span class="se-tooltip-inner">
2689
- <span class="se-tooltip-text">${text}</span>
2690
- </span>
2691
- </button>
2692
- </li>`;
3857
+ <button type="button" class="se-btn se-tooltip" data-command="props_align" data-value="${item}" title="${text}" aria-label="${text}">
3858
+ ${icons['align_' + item]}
3859
+ <span class="se-tooltip-inner">
3860
+ <span class="se-tooltip-text">${text}</span>
3861
+ </span>
3862
+ </button>`;
2693
3863
  }
2694
3864
 
2695
3865
  // vertical align html
@@ -2699,14 +3869,12 @@ function CreateHTML_controller_properties({ lang, icons, options }) {
2699
3869
  item = verticalAligns[i];
2700
3870
  text = lang['align' + item.charAt(0).toUpperCase() + item.slice(1)];
2701
3871
  verticalAlignHtml += /*html*/ `
2702
- <li>
2703
- <button type="button" class="se-btn se-btn-list se-tooltip" data-command="props_align_vertical" data-value="${item}" title="${text}" aria-label="${text}">
2704
- ${icons['align_' + item]}
2705
- <span class="se-tooltip-inner">
2706
- <span class="se-tooltip-text">${text}</span>
2707
- </span>
2708
- </button>
2709
- </li>`;
3872
+ <button type="button" class="se-btn se-tooltip" data-command="props_align_vertical" data-value="${item}" title="${text}" aria-label="${text}">
3873
+ ${icons['align_' + item]}
3874
+ <span class="se-tooltip-inner">
3875
+ <span class="se-tooltip-text">${text}</span>
3876
+ </span>
3877
+ </button>`;
2710
3878
  }
2711
3879
 
2712
3880
  const html = /*html*/ `
@@ -2789,12 +3957,14 @@ function CreateHTML_controller_properties({ lang, icons, options }) {
2789
3957
  </div>
2790
3958
 
2791
3959
  <div class="se-table-props-align">
2792
- <label>${lang.align}</label>
2793
- <div class="se-form-group se-form-w0 se-list-inner __se__a_h">
2794
- <ul class="se-form-group se-form-w0">${alignHtml}</ul>
2795
- </div>
2796
- <div class="se-form-group se-form-w0 se-list-inner __se__a_v">
2797
- <ul class="se-form-group se-form-w0">${verticalAlignHtml}</ul>
3960
+ <label>${lang.align} <span class="__se__a_table_t">( ${lang.table} )</span></label>
3961
+ <div class="se-form-group se-form-w0 se-list-inner">
3962
+ <div class="__se__a_h">
3963
+ ${alignHtml}
3964
+ </div>
3965
+ <div class="__se__a_v">
3966
+ ${verticalAlignHtml}
3967
+ </div>
2798
3968
  </div>
2799
3969
  </div>
2800
3970
  </div>
@@ -2804,7 +3974,27 @@ function CreateHTML_controller_properties({ lang, icons, options }) {
2804
3974
  </div>
2805
3975
  </div>`;
2806
3976
 
2807
- return domUtils.createElement('DIV', { class: 'se-controller se-table-props' }, html);
3977
+ const content = dom.utils.createElement('DIV', { class: 'se-controller se-table-props' }, html);
3978
+
3979
+ return {
3980
+ html: content,
3981
+ controller_props_title: content.querySelector('.se-controller-title'),
3982
+ borderButton: content.querySelector('[data-command="props_onborder_style"]'),
3983
+ borderFormatButton: content.querySelector('[data-command="props_onborder_format"]'),
3984
+ cell_alignment: content.querySelector('.se-table-props-align .__se__a_h'),
3985
+ cell_alignment_vertical: content.querySelector('.se-table-props-align .__se__a_v'),
3986
+ cell_alignment_table_text: content.querySelector('.se-table-props-align .__se__a_table_t'),
3987
+ border_style: content.querySelector('[data-command="props_onborder_style"] .se-txt'),
3988
+ border_color: content.querySelector('.__se_border_color'),
3989
+ border_width: content.querySelector('.__se__border_size'),
3990
+ back_color: content.querySelector('.__se_back_color'),
3991
+ font_color: content.querySelector('.__se_font_color'),
3992
+ palette_border_button: content.querySelector('[data-command="props_onpalette"][data-value="border"]'),
3993
+ font_bold: content.querySelector('[data-command="props_font_style"][data-value="bold"]'),
3994
+ font_underline: content.querySelector('[data-command="props_font_style"][data-value="underline"]'),
3995
+ font_italic: content.querySelector('[data-command="props_font_style"][data-value="italic"]'),
3996
+ font_strike: content.querySelector('[data-command="props_font_style"][data-value="strike"]')
3997
+ };
2808
3998
  }
2809
3999
 
2810
4000
  export default Table;