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
@@ -3,39 +3,85 @@
3
3
  */
4
4
 
5
5
  import CoreInjector from '../../editorInjector/_core';
6
- import { domUtils, env, numbers, unicode } from '../../helper';
6
+ import { dom, env, numbers, unicode, keyCodeMap, converter } from '../../helper';
7
7
  import { Figure, _DragHandle } from '../../modules';
8
8
 
9
9
  const { _w, ON_OVER_COMPONENT, isMobile } = env;
10
10
  const DIR_KEYCODE = /^(3[7-9]|40)$/;
11
11
  const DIR_UP_KEYCODE = /^3[7-8]$/;
12
12
 
13
- const Component = function (editor) {
13
+ /**
14
+ * @typedef {Omit<Component & Partial<__se__EditorInjector>, 'component'>} ComponentThis
15
+ */
16
+
17
+ /**
18
+ * @constructor
19
+ * @this {ComponentThis}
20
+ * @description Class for managing components such as images and tables that are not in line format
21
+ * @param {__se__EditorCore} editor - The root editor instance
22
+ */
23
+ function Component(editor) {
14
24
  CoreInjector.call(this, editor);
15
25
 
16
- // members
26
+ /**
27
+ * @description The current component information, used copy, cut, and keydown events
28
+ * @type {__se__ComponentInfo}
29
+ */
17
30
  this.info = null;
31
+
32
+ /**
33
+ * @description Component is selected
34
+ * @type {boolean}
35
+ */
18
36
  this.isSelected = false;
37
+
38
+ /**
39
+ * @description Currently selected component target
40
+ * @type {Node|null}
41
+ */
19
42
  this.currentTarget = null;
43
+
44
+ /**
45
+ * @description Currently selected component plugin instance
46
+ * @type {*}
47
+ */
20
48
  this.currentPlugin = null;
49
+
50
+ /**
51
+ * @description Currently selected component plugin name
52
+ * @type {*}
53
+ */
21
54
  this.currentPluginName = '';
55
+
56
+ /**
57
+ * @description Currently selected component information
58
+ * @type {__se__ComponentInfo|null}
59
+ */
22
60
  this.currentInfo = null;
61
+
62
+ /** @type {Object<string, (...args: *) => *>} */
23
63
  this.__globalEvents = {
24
64
  copy: OnCopy_component.bind(this),
25
65
  cut: OnCut_component.bind(this),
26
66
  keydown: OnKeyDown_component.bind(this),
27
67
  mousedown: CloseListener_mousedown.bind(this)
28
68
  };
69
+ /** @type {__se__GlobalEventInfo|void} */
29
70
  this._bindClose_copy = null;
71
+ /** @type {__se__GlobalEventInfo|void} */
30
72
  this._bindClose_cut = null;
73
+ /** @type {__se__GlobalEventInfo|void} */
31
74
  this._bindClose_keydown = null;
75
+ /** @type {__se__GlobalEventInfo|void} */
32
76
  this._bindClose_mousedown = null;
77
+ /** @type {__se__GlobalEventInfo|void} */
33
78
  this._bindClose_touchstart = null;
79
+ /** @type {boolean} */
34
80
  this.__selectionSelected = false;
35
81
 
36
82
  this.editor.applyFrameRoots((e) => {
37
83
  // drag
38
- const dragHandle = domUtils.createElement('DIV', { class: 'se-drag-handle', draggable: 'true' }, this.icons.selection);
84
+ const dragHandle = dom.utils.createElement('DIV', { class: 'se-drag-handle', draggable: 'true' }, this.icons.selection);
39
85
  e.get('wrapper').appendChild(dragHandle);
40
86
  this.eventManager.addEvent(dragHandle, 'mouseenter', OnDragEnter.bind(this));
41
87
  this.eventManager.addEvent(dragHandle, 'mouseleave', OnDragLeave.bind(this));
@@ -43,19 +89,22 @@ const Component = function (editor) {
43
89
  this.eventManager.addEvent(dragHandle, 'dragend', OnDragEnd.bind(this));
44
90
  this.eventManager.addEvent(dragHandle, 'click', OnDragClick.bind(this));
45
91
  });
46
- };
92
+ }
47
93
 
48
94
  Component.prototype = {
49
95
  /**
50
- * @description The method to insert a element and return. (used elements : table, hr, image, video)
51
- * If "element" is "HR", insert and return the new line.
52
- * @param {Element} element Element to be inserted
53
- * @param {boolean} notCheckCharCount If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
54
- * @param {boolean} notSelect If true, Do not automatically select the inserted component.
55
- * @returns {Element}
96
+ * @this {ComponentThis}
97
+ * @description Inserts an element and returns it. (Used for elements: table, hr, image, video)
98
+ * - If "element" is "HR", inserts and returns the new line.
99
+ * @param {Node} element Element to be inserted
100
+ * @param {Object} [options] Options
101
+ * @param {boolean} [options.skipCharCount=false] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
102
+ * @param {boolean} [options.skipSelection=false] If true, do not automatically select the inserted component.
103
+ * @param {boolean} [options.skipHistory=false] If true, do not push to history.
104
+ * @returns {HTMLElement} The inserted element or new line (for HR)
56
105
  */
57
- insert(element, notCheckCharCount, notSelect) {
58
- if (this.editor.frameContext.get('isReadOnly') || (!notCheckCharCount && !this.char.check(element))) {
106
+ insert(element, { skipCharCount, skipSelection, skipHistory } = {}) {
107
+ if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(element))) {
59
108
  return null;
60
109
  }
61
110
 
@@ -66,45 +115,51 @@ Component.prototype = {
66
115
  let oNode = null;
67
116
  let formatEl = this.format.getLine(selectionNode, null);
68
117
 
69
- if (domUtils.isListCell(formatEl)) {
70
- this.html.insertNode(element, isInline ? null : selectionNode === formatEl ? null : r.container.nextSibling, true);
71
- if (!isInline && !element.nextSibling) element.parentNode.appendChild(domUtils.createElement('BR'));
118
+ if (dom.check.isListCell(formatEl)) {
119
+ this.html.insertNode(element, { afterNode: isInline ? null : selectionNode === formatEl ? null : r.container.nextSibling, skipCharCount: true });
120
+ if (!isInline && !element.nextSibling) element.parentNode.appendChild(dom.utils.createElement('BR'));
72
121
  } else {
73
- if (this.selection.getRange().collapsed && (r.container.nodeType === 3 || domUtils.isBreak(r.container))) {
74
- const depthFormat = domUtils.getParentElement(r.container, this.format.isBlock.bind(this.format));
75
- oNode = this.nodeTransform.split(r.container, r.offset, !depthFormat ? 0 : domUtils.getNodeDepth(depthFormat) + 1);
76
- if (oNode) formatEl = oNode.previousSibling;
122
+ if (!isInline && this.selection.getRange().collapsed && (r.container.nodeType === 3 || dom.check.isBreak(r.container))) {
123
+ const depthFormat = dom.query.getParentElement(r.container, this.format.isBlock.bind(this.format));
124
+ oNode = this.nodeTransform.split(r.container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
125
+ if (oNode) formatEl = /** @type {HTMLElement} */ (oNode.previousSibling);
77
126
  }
78
- this.html.insertNode(element, isInline ? null : this.format.isBlock(formatEl) ? null : formatEl, true);
79
- if (!isInline && formatEl && domUtils.isZeroWith(formatEl)) domUtils.removeItem(formatEl);
127
+ this.html.insertNode(element, { afterNode: isInline ? null : this.format.isBlock(formatEl) ? null : formatEl, skipCharCount: true });
128
+ if (!isInline && formatEl && dom.check.isZeroWidth(formatEl)) dom.utils.removeItem(formatEl);
80
129
  }
81
130
 
82
131
  if (isInline) {
83
- const empty = domUtils.createTextNode(unicode.zeroWidthSpace);
132
+ const empty = dom.utils.createTextNode(unicode.zeroWidthSpace);
84
133
  element.parentNode.insertBefore(empty, element.nextSibling);
85
134
  }
86
135
 
87
- this.history.push(false);
136
+ if (!skipHistory) this.history.push(false);
88
137
 
89
- if (!notSelect) {
138
+ if (!skipSelection) {
90
139
  this.selection.setRange(element, 0, element, 0);
91
140
  const fileComponentInfo = this.get(element);
92
141
  if (fileComponentInfo) {
93
142
  this.select(fileComponentInfo.target, fileComponentInfo.pluginName);
94
143
  } else if (oNode) {
95
- oNode = domUtils.getEdgeChildNodes(oNode, null).sc || oNode;
144
+ oNode = dom.query.getEdgeChildNodes(oNode, null).sc || oNode;
96
145
  this.selection.setRange(oNode, 0, oNode, 0);
97
146
  }
98
147
  }
99
148
 
100
- return oNode || element;
149
+ // document type
150
+ if (this.editor.frameContext.has('documentType-use-header')) {
151
+ this.editor.frameContext.get('documentType').reHeader();
152
+ }
153
+
154
+ return /** @type {HTMLElement} */ (oNode || element);
101
155
  },
102
156
 
103
157
  /**
158
+ * @this {ComponentThis}
104
159
  * @description Gets the file component and that plugin name
105
- * return: {target, component, pluginName} | null
106
- * @param {Element} element Target element (figure tag, component div, file tag)
107
- * @returns {Object|null}
160
+ * - return: {target, component, pluginName} | null
161
+ * @param {Node} element Target element (figure tag, component div, file tag)
162
+ * @returns {__se__ComponentInfo|null}
108
163
  */
109
164
  get(element) {
110
165
  if (!element) return null;
@@ -113,10 +168,11 @@ Component.prototype = {
113
168
  let pluginName = '';
114
169
  let options = {};
115
170
  let isFile = false;
171
+ let launcher = null;
116
172
 
117
173
  if (this.is(element)) {
118
- if (domUtils.hasClass(element, 'se-component') && !domUtils.hasClass(element, 'se-inline-component')) element = element.firstElementChild || element;
119
- if (/^FIGURE$/i.test(element.nodeName)) element = element.firstElementChild;
174
+ if (dom.utils.hasClass(element, 'se-component') && !dom.utils.hasClass(element, 'se-inline-component')) element = /** @type {HTMLElement} */ (element).firstElementChild || element;
175
+ if (/^FIGURE$/i.test(element.nodeName)) element = /** @type {HTMLElement} */ (element).firstElementChild;
120
176
  if (!element) return null;
121
177
 
122
178
  const comp = this.editor._componentManager.map((f) => f(element)).find((e) => e);
@@ -124,6 +180,7 @@ Component.prototype = {
124
180
  target = comp.target;
125
181
  pluginName = comp.pluginName;
126
182
  options = comp.options;
183
+ launcher = comp.launcher;
127
184
  }
128
185
 
129
186
  if (!target && element.nodeName) {
@@ -135,6 +192,7 @@ Component.prototype = {
135
192
  target = comp.target;
136
193
  pluginName = comp.pluginName;
137
194
  options = comp.options;
195
+ launcher = comp.launcher;
138
196
  }
139
197
 
140
198
  if (!target) {
@@ -142,31 +200,40 @@ Component.prototype = {
142
200
  }
143
201
 
144
202
  const figureInfo = Figure.GetContainer(target);
203
+ const container = figureInfo.container || figureInfo.cover || target;
145
204
  return (this.info = {
146
205
  target,
147
206
  pluginName,
148
207
  options,
149
- container: figureInfo.container || figureInfo.cover || target,
208
+ container: container,
150
209
  cover: figureInfo.cover,
210
+ inlineCover: figureInfo.inlineCover,
151
211
  caption: figureInfo.caption,
152
- isFile: isFile
212
+ isFile,
213
+ launcher,
214
+ isInputType: dom.utils.hasClass(container, 'se-input-component')
153
215
  });
154
216
  },
155
217
 
156
218
  /**
157
- * @description The component(image, video) is selected and the resizing module is called.
158
- * @param {Element} element Element tag (img, iframe, video)
159
- * @param {string} pluginName Plugin name (image, video)
219
+ * @this {ComponentThis}
220
+ * @description The component(media, file component, table, etc) is selected and the resizing module is called.
221
+ * @param {Node} element Target element
222
+ * @param {string} pluginName The plugin name for the selected target.
223
+ * @param {Object} [options] Options
224
+ * @param {boolean} [options.isInput=false] Whether the target is an input component.(table)
160
225
  */
161
- select(element, pluginName, isInput) {
226
+ select(element, pluginName, { isInput = false } = {}) {
162
227
  const info = this.get(element);
163
- if (!info || domUtils.isUneditable(domUtils.getParentElement(element, this.is.bind(this))) || domUtils.isUneditable(element)) return false;
228
+ if (!info || dom.check.isUneditable(dom.query.getParentElement(element, this.is.bind(this))) || dom.check.isUneditable(element)) return false;
164
229
 
165
- const plugin = this.plugins[pluginName];
230
+ const plugin = info.launcher || this.plugins[pluginName];
166
231
  if (!plugin) return;
167
232
 
168
233
  if (!isInput && _DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
169
- this.editor._antiBlur = true;
234
+ if (this.editor.status._onMousedown) return;
235
+
236
+ this.editor._preventBlur = true;
170
237
  this.__selectionSelected = true;
171
238
  if (this.isInline(info.container)) {
172
239
  this.selection.setRange(info.container, 0, info.container, 0);
@@ -182,7 +249,8 @@ Component.prototype = {
182
249
  let isNonFigureComponent;
183
250
  if (typeof plugin.select === 'function') isNonFigureComponent = plugin.select(element);
184
251
 
185
- if (!isNonFigureComponent && !domUtils.hasClass(info.container, 'se-inline-component')) this._setComponentLineBreaker(info.container || info.cover || element);
252
+ const isBreakComponent = dom.utils.hasClass(info.target, 'se-component-line-break');
253
+ if (isBreakComponent || (!isNonFigureComponent && !dom.utils.hasClass(info.container, 'se-inline-component'))) this._setComponentLineBreaker(/** @type {HTMLElement} */ (info.container || info.cover || element));
186
254
 
187
255
  this.currentTarget = element;
188
256
  this.currentPlugin = plugin;
@@ -197,31 +265,32 @@ Component.prototype = {
197
265
  if (__overInfo !== ON_OVER_COMPONENT) this.__addGlobalEvent();
198
266
  if (!info.isFile) this.__addNotFileGlobalEvent();
199
267
  }, 0);
200
- domUtils.addClass(info.container, 'se-component-selected');
201
268
 
202
- if (__overInfo !== ON_OVER_COMPONENT) {
203
- domUtils.setDisabled(this.editor._controllerOnDisabledButtons, true);
269
+ converter.debounce(() => {
270
+ dom.utils.addClass(info.container, 'se-component-selected');
271
+ }, 0)();
204
272
 
273
+ if (!isBreakComponent && __overInfo !== ON_OVER_COMPONENT) {
205
274
  // set zero width space
206
275
  if (!this.isInline(info.container)) return;
207
276
 
208
277
  const oNode = info.container;
209
278
  let zeroWidth = null;
210
- if (!oNode.previousSibling || domUtils.isBreak(oNode.previousSibling)) {
211
- zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
279
+ if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
280
+ zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
212
281
  oNode.parentNode.insertBefore(zeroWidth, oNode);
213
282
  }
214
283
 
215
- if (!oNode.nextSibling || domUtils.isBreak(oNode.nextSibling)) {
216
- zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
284
+ if (!oNode.nextSibling || dom.check.isBreak(oNode.nextSibling)) {
285
+ zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
217
286
  oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
218
287
  }
219
288
 
220
- this.editor.status.componentSelected = true;
221
- } else if (!domUtils.hasClass(info.container, 'se-input-component')) {
289
+ this.editor.status.onSelected = true;
290
+ } else if (isBreakComponent || !dom.utils.hasClass(info.container, 'se-input-component')) {
222
291
  const dragHandle = this.editor.frameContext.get('wrapper').querySelector('.se-drag-handle');
223
- domUtils.addClass(dragHandle, 'se-drag-handle-full');
224
- this.editor._visibleControllers(false, false);
292
+ dom.utils.addClass(dragHandle, 'se-drag-handle-full');
293
+ this.ui._visibleControllers(false, false);
225
294
 
226
295
  const sizeTarget = info.caption ? info.target : info.cover || info.container || info.target;
227
296
  const w = sizeTarget.offsetWidth;
@@ -242,32 +311,40 @@ Component.prototype = {
242
311
  }
243
312
  },
244
313
 
314
+ /**
315
+ * @this {ComponentThis}
316
+ * @description Deselects the selected component.
317
+ */
245
318
  deselect() {
246
- this.editor.status.componentSelected = false;
319
+ _w.setTimeout(() => {
320
+ this.editor.status.onSelected = false;
321
+ }, 0);
247
322
  this.__deselect();
248
- domUtils.setDisabled(this.editor._controllerOnDisabledButtons, false);
323
+ this.ui.setControllerOnDisabledButtons(false);
249
324
  },
250
325
 
251
326
  /**
327
+ * @this {ComponentThis}
252
328
  * @description Determines if the specified node is a block component (e.g., img, iframe, video, audio, table) with the class "se-component"
253
- * or a direct FIGURE node. This function checks if the node itself is a component
254
- * or if it belongs to any components identified by the component manager.
329
+ * - or a direct FIGURE node. This function checks if the node itself is a component
330
+ * - or if it belongs to any components identified by the component manager.
255
331
  * @param {Node} element The DOM node to check.
256
332
  * @returns {boolean} True if the node is a block component or part of it, otherwise false.
257
333
  */
258
334
  is(element) {
259
335
  if (!element) return false;
260
336
 
261
- if (/^FIGURE$/i.test(element.nodeName) || domUtils.hasClass(element, 'se-component')) return true;
337
+ if (/^FIGURE$/i.test(element.nodeName) || dom.utils.hasClass(element, 'se-component')) return true;
262
338
  if (this.editor._componentManager.find((f) => f(element))) return true;
263
339
 
264
340
  return false;
265
341
  },
266
342
 
267
343
  /**
344
+ * @this {ComponentThis}
268
345
  * @description Checks if the given node is an inline component (class "se-inline-component").
269
- * If the node is a FIGURE, it checks the parent element instead.
270
- * It also verifies whether the node is part of an inline component recognized by the component manager.
346
+ * - If the node is a FIGURE, it checks the parent element instead.
347
+ * - It also verifies whether the node is part of an inline component recognized by the component manager.
271
348
  * @param {Node} element The DOM node to check.
272
349
  * @returns {boolean} True if the node is an inline component or part of it, otherwise false.
273
350
  */
@@ -275,18 +352,19 @@ Component.prototype = {
275
352
  if (!element) return false;
276
353
 
277
354
  if (/^FIGURE$/i.test(element.nodeName)) element = element.parentElement;
278
- if (domUtils.hasClass(element, 'se-inline-component')) return true;
355
+ if (dom.utils.hasClass(element, 'se-inline-component')) return true;
279
356
 
280
357
  const container = this.editor._componentManager.find((f) => f(element));
281
- if (container && domUtils.hasClass(element, 'se-inline-component')) return true;
358
+ if (container && dom.utils.hasClass(element, 'se-inline-component')) return true;
282
359
 
283
360
  return false;
284
361
  },
285
362
 
286
363
  /**
364
+ * @this {ComponentThis}
287
365
  * @description Checks if the specified node qualifies as a basic component within the editor.
288
- * This function verifies whether the node is recognized as a component by the `is` function, while also ensuring that it is not an inline component as determined by the `isInline` function.
289
- * This is used to identify block-level elements or standalone components that are not part of the inline component classification.
366
+ * - This function verifies whether the node is recognized as a component by the `is` function, while also ensuring that it is not an inline component as determined by the `isInline` function.
367
+ * - This is used to identify block-level elements or standalone components that are not part of the inline component classification.
290
368
  * @param {Node} element The DOM node to check.
291
369
  * @returns {boolean} True if the node is a basic (non-inline) component, otherwise false.
292
370
  */
@@ -294,17 +372,60 @@ Component.prototype = {
294
372
  return this.is(element) && !this.isInline(element);
295
373
  },
296
374
 
375
+ /**
376
+ * @this {ComponentThis}
377
+ * @description Copies the specified component node to the clipboard.
378
+ * - This function is different from the one called when the user presses the "Ctrl + C" key combination.
379
+ * @param {Node} container The DOM node to check.
380
+ */
381
+ copy(container) {
382
+ const cloneContainer = /** @type {HTMLElement} */ (dom.utils.clone(container, true));
383
+
384
+ // remove selected class
385
+ dom.utils.removeClass(cloneContainer, 'se-component-selected');
386
+ dom.utils.removeClass(cloneContainer.querySelectorAll('.se-figure-selected'), 'se-figure-selected');
387
+ dom.utils.removeClass(cloneContainer.querySelectorAll('.se-selected-table-cell'), 'se-selected-table-cell');
388
+ dom.utils.removeClass(cloneContainer.querySelector('.se-selected-cell-focus'), 'se-selected-cell-focus');
389
+
390
+ // copy to clipboard
391
+ this.html.copy(cloneContainer);
392
+
393
+ // copy effect
394
+ dom.utils.flashClass(container, 'se-copy');
395
+ },
396
+
397
+ /**
398
+ * @private
399
+ * @this {ComponentThis}
400
+ * @description Checks if the given element is a file component by matching its tag name against the file manager's regular expressions.
401
+ * - It also verifies whether the element has the required attributes based on the tag type.
402
+ * @param {Node} element The element to check.
403
+ * @returns {boolean} Returns true if the element is a file component, otherwise false.
404
+ */
297
405
  __isFiles(element) {
298
406
  const nodeName = element.nodeName.toLowerCase();
299
- return this.editor._fileManager.regExp.test(nodeName) && (!this.editor._fileManager.tagAttrs[nodeName] || this.editor._fileManager.tagAttrs[nodeName]?.every((v) => element.hasAttribute(v)));
407
+ return this.editor._fileManager.regExp.test(nodeName) && (!this.editor._fileManager.tagAttrs[nodeName] || this.editor._fileManager.tagAttrs[nodeName]?.every((v) => /** @type {HTMLElement} */ (element).hasAttribute(v)));
300
408
  },
301
409
 
410
+ /**
411
+ * @private
412
+ * @this {ComponentThis}
413
+ * @description Deselects the currently selected component, removing any selection effects and associated event listeners.
414
+ * - This method resets the selection state and hides UI elements related to the component selection.
415
+ */
302
416
  __deselect() {
303
- this.editor._antiBlur = false;
417
+ this.editor._preventBlur = false;
304
418
  _DragHandle.set('__overInfo', null);
305
- this._removeDragEvent(this);
306
- domUtils.removeClass(this.currentInfo?.container, 'se-component-selected|');
307
- domUtils.removeClass(this.currentInfo?.cover, 'se-figure-over-selected');
419
+ this._removeDragEvent();
420
+
421
+ if (this.currentInfo) {
422
+ const infoContainer = this.currentInfo.container;
423
+ const infoCover = this.currentInfo.cover;
424
+ converter.debounce(() => {
425
+ dom.utils.removeClass(infoContainer, 'se-component-selected');
426
+ dom.utils.removeClass(infoCover, 'se-figure-over-selected');
427
+ }, 0)();
428
+ }
308
429
 
309
430
  const { frameContext } = this.editor;
310
431
  frameContext.get('lineBreaker_t').style.display = frameContext.get('lineBreaker_b').style.display = 'none';
@@ -319,47 +440,58 @@ Component.prototype = {
319
440
  this.currentPluginName = '';
320
441
  this.currentInfo = null;
321
442
  this.__removeGlobalEvent();
322
- this.editor.__offControllers();
443
+ this.ui.__offControllers();
323
444
  },
324
445
 
325
446
  /**
326
- * @description Set line breaker of component
327
- * @param {Element} element Element tag
328
447
  * @private
448
+ * @this {ComponentThis}
449
+ * @description Set line breaker of component
450
+ * @param {HTMLElement} element Element tag
329
451
  */
330
452
  _setComponentLineBreaker(element) {
453
+ const _overInfo = _DragHandle.get('__overInfo') === ON_OVER_COMPONENT;
331
454
  this.eventManager._lineBreakComp = null;
332
455
  const info = this.get(element);
333
456
  if (!info) return;
334
457
 
335
458
  const fc = this.editor.frameContext;
336
459
  const container = info.container;
337
- const isNonSelected = domUtils.hasClass(container, 'se-flex-component');
460
+ const isNonSelected = dom.utils.hasClass(container, 'se-flex-component');
338
461
  const lb_t = fc.get('lineBreaker_t');
339
462
  const lb_b = fc.get('lineBreaker_b');
340
463
  const t_style = lb_t.style;
341
464
  const b_style = lb_b.style;
342
465
  const offsetTarget = container.offsetWidth < element.offsetWidth ? container : element;
343
466
  const target = this.editor._figureContainer?.style.display === 'block' ? this.editor._figureContainer : offsetTarget;
344
- const isList = domUtils.isListCell(container.parentNode);
467
+ const isList = dom.check.isListCell(container.parentNode);
345
468
 
346
469
  // top
347
470
  let componentTop, w;
348
471
  const isRtl = this.options.get('_rtl');
349
472
  const dir = isRtl ? ['right', 'left'] : ['left', 'right'];
350
- const top = offsetTarget.offsetTop;
351
- const { scrollX, scrollY } = this.offset.getLocal(offsetTarget);
473
+ const { top, left, right, scrollX, scrollY } = this.offset.getLocal(offsetTarget);
474
+ const sideOffset = isRtl ? right : left;
352
475
 
353
476
  if (isList ? !container.previousSibling : !this.format.isLine(container.previousElementSibling)) {
354
- const tH = numbers.get(_w.getComputedStyle(lb_t).height, 1);
477
+ const cStyle = _w.getComputedStyle(lb_t);
478
+ const cH = numbers.get(cStyle.height, 1);
479
+ const cW = numbers.get(cStyle.width, 1);
480
+
355
481
  this.eventManager._lineBreakComp = container;
356
482
  componentTop = top;
357
483
  w = target.offsetWidth / 2 / 2;
358
- t_style.top = componentTop - scrollY - tH / 2 + 'px';
359
- t_style[dir[0]] = (isNonSelected ? 4 : this.offset.get(target).left + w) + 'px';
484
+
485
+ t_style.top = componentTop - scrollY - cH / 2 + 'px';
486
+ t_style[dir[0]] = (isNonSelected ? sideOffset - cW / 2 : sideOffset + w) + 'px';
360
487
  t_style[dir[1]] = '';
488
+
361
489
  lb_t.setAttribute('data-offset', scrollY + ',' + scrollX);
490
+ if (_overInfo) dom.utils.removeClass(lb_t, 'se-on-selected');
491
+ else dom.utils.addClass(lb_t, 'se-on-selected');
492
+
362
493
  t_style.display = 'block';
494
+ t_style.visibility = '';
363
495
  } else {
364
496
  t_style.display = 'none';
365
497
  }
@@ -367,8 +499,8 @@ Component.prototype = {
367
499
  // bottom
368
500
  if (isList ? !container.nextSibling : !this.format.isLine(container.nextElementSibling)) {
369
501
  const cStyle = _w.getComputedStyle(lb_b);
370
- const bH = numbers.get(cStyle.height, 1);
371
- const bW = numbers.get(cStyle.width, 1);
502
+ const cH = numbers.get(cStyle.height, 1);
503
+ const cW = numbers.get(cStyle.width, 1);
372
504
 
373
505
  if (!componentTop) {
374
506
  this.eventManager._lineBreakComp = container;
@@ -376,18 +508,26 @@ Component.prototype = {
376
508
  w = target.offsetWidth / 2 / 2;
377
509
  }
378
510
 
379
- b_style.top = componentTop + target.offsetHeight - scrollY - bH / 2 + 'px';
380
- b_style.right = '';
381
- b_style.left = this.offset.get(target).left + (isRtl ? 0 : target.offsetWidth) - (isNonSelected ? 0 : w) - (isNonSelected ? bW / 2 : bW) + 'px';
511
+ b_style.top = componentTop + target.offsetHeight - scrollY - cH / 2 + 'px';
512
+ b_style[dir[0]] = sideOffset + target.offsetWidth - (isNonSelected ? 0 : w) - (isNonSelected ? cW / 2 : cW) + 'px';
513
+ b_style[dir[1]] = '';
514
+
515
+ lb_b.setAttribute('data-offset', scrollY + ',' + dir[0] + ',' + scrollX);
516
+ if (_overInfo) dom.utils.removeClass(lb_b, 'se-on-selected');
517
+ else dom.utils.addClass(lb_b, 'se-on-selected');
382
518
 
383
- const bDir = 'left';
384
- lb_b.setAttribute('data-offset', scrollY + ',' + bDir + ',' + scrollX);
385
519
  b_style.display = 'block';
520
+ b_style.visibility = '';
386
521
  } else {
387
522
  b_style.display = 'none';
388
523
  }
389
524
  },
390
525
 
526
+ /**
527
+ * @private
528
+ * @this {ComponentThis}
529
+ * @description Adds global event listeners for component interactions such as copy, cut, and keydown events.
530
+ */
391
531
  __addGlobalEvent() {
392
532
  this.__removeGlobalEvent();
393
533
  this._bindClose_copy = this.eventManager.addGlobalEvent('copy', this.__globalEvents.copy);
@@ -395,6 +535,11 @@ Component.prototype = {
395
535
  this._bindClose_keydown = this.eventManager.addGlobalEvent('keydown', this.__globalEvents.keydown);
396
536
  },
397
537
 
538
+ /**
539
+ * @private
540
+ * @this {ComponentThis}
541
+ * @description Removes global event listeners that were previously added for component interactions.
542
+ */
398
543
  __removeGlobalEvent() {
399
544
  this.__removeNotFileGlobalEvent();
400
545
  if (this._bindClose_copy) this._bindClose_copy = this.eventManager.removeGlobalEvent(this._bindClose_copy);
@@ -402,23 +547,38 @@ Component.prototype = {
402
547
  if (this._bindClose_keydown) this._bindClose_keydown = this.eventManager.removeGlobalEvent(this._bindClose_keydown);
403
548
  },
404
549
 
550
+ /**
551
+ * @private
552
+ * @this {ComponentThis}
553
+ * @description Adds global event listeners for non-file-related interactions such as mouse and touch events.
554
+ */
405
555
  __addNotFileGlobalEvent() {
406
556
  this.__removeNotFileGlobalEvent();
407
557
  if (!isMobile) this._bindClose_mousedown = this.eventManager.addGlobalEvent('mousedown', this.__globalEvents.mousedown, true);
408
558
  else this._bindClose_touchstart = this.eventManager.addGlobalEvent('touchstart', this.__globalEvents.mousedown, true);
409
559
  },
410
560
 
561
+ /**
562
+ * @private
563
+ * @this {ComponentThis}
564
+ * @description Removes global event listeners related to non-file interactions.
565
+ */
411
566
  __removeNotFileGlobalEvent() {
412
567
  if (this._bindClose_mousedown) this._bindClose_mousedown = this.eventManager.removeGlobalEvent(this._bindClose_mousedown);
413
568
  if (this._bindClose_touchstart) this._bindClose_touchstart = this.eventManager.removeGlobalEvent(this._bindClose_touchstart);
414
569
  },
415
570
 
571
+ /**
572
+ * @private
573
+ * @this {ComponentThis}
574
+ * @description Removes drag-related events and resets drag-related states.
575
+ */
416
576
  _removeDragEvent() {
417
- this.carrierWrapper.querySelector('.se-drag-cursor').style.left = '-10000px';
577
+ /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-drag-cursor')).style.left = '-10000px';
418
578
  if (_DragHandle.get('__dragHandler')) _DragHandle.get('__dragHandler').style.display = 'none';
419
579
 
420
- domUtils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
421
- domUtils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
580
+ dom.utils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
581
+ dom.utils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
422
582
 
423
583
  _DragHandle.set('__figureInst', null);
424
584
  _DragHandle.set('__dragInst', null);
@@ -432,18 +592,28 @@ Component.prototype = {
432
592
  constructor: Component
433
593
  };
434
594
 
595
+ /**
596
+ * @this {ComponentThis}
597
+ */
435
598
  function OnDragEnter() {
436
- this.editor._antiBlur = true;
437
- this.editor._visibleControllers(false, domUtils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full'));
438
- domUtils.addClass(_DragHandle.get('__dragCover') || _DragHandle.get('__dragContainer'), 'se-drag-over');
599
+ this.editor._preventBlur = true;
600
+ this.ui._visibleControllers(false, dom.utils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full'));
601
+ dom.utils.addClass(_DragHandle.get('__dragCover') || _DragHandle.get('__dragContainer'), 'se-drag-over');
439
602
  }
440
603
 
604
+ /**
605
+ * @this {ComponentThis}
606
+ */
441
607
  function OnDragLeave() {
442
- this.editor._antiBlur = false;
443
- if (!domUtils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full')) this.editor._visibleControllers(true, true);
444
- domUtils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
608
+ this.editor._preventBlur = false;
609
+ if (!dom.utils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full')) this.ui._visibleControllers(true, true);
610
+ dom.utils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
445
611
  }
446
612
 
613
+ /**
614
+ * @this {ComponentThis}
615
+ * @param {DragEvent} e - Drag event
616
+ */
447
617
  function OnDragStart(e) {
448
618
  const cover = _DragHandle.get('__dragCover') || _DragHandle.get('__dragContainer');
449
619
 
@@ -452,29 +622,44 @@ function OnDragStart(e) {
452
622
  return;
453
623
  }
454
624
 
455
- this.editor._antiBlur = false;
456
- domUtils.addClass(_DragHandle.get('__dragHandler'), 'se-dragging');
457
- domUtils.addClass(_DragHandle.get('__dragContainer'), 'se-dragging');
625
+ this.editor._preventBlur = false;
626
+ dom.utils.addClass(_DragHandle.get('__dragHandler'), 'se-dragging');
627
+ dom.utils.addClass(_DragHandle.get('__dragContainer'), 'se-dragging');
458
628
  e.dataTransfer.setDragImage(cover, this.options.get('_rtl') ? cover.offsetWidth : -5, -5);
459
629
  }
460
630
 
631
+ /**
632
+ * @this {ComponentThis}
633
+ */
461
634
  function OnDragEnd() {
462
- this.editor._antiBlur = false;
463
- domUtils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
635
+ this.editor._preventBlur = false;
636
+ dom.utils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
464
637
  this._removeDragEvent();
465
638
  }
466
639
 
467
- function OnDragClick({ target }) {
468
- if (!domUtils.hasClass(target, 'se-drag-handle-full')) return;
640
+ /**
641
+ * @this {ComponentThis}
642
+ * @param {MouseEvent} e - Mouse event
643
+ */
644
+ function OnDragClick(e) {
645
+ const target = dom.query.getEventTarget(e);
646
+ if (!dom.utils.hasClass(target, 'se-drag-handle-full')) return;
647
+
469
648
  const dragInst = _DragHandle.get('__dragInst');
470
- this.select(dragInst.currentTarget, dragInst.currentPluginName, false);
649
+ this._removeDragEvent();
650
+ this.select(dragInst.currentTarget, dragInst.currentPluginName);
471
651
  }
472
652
 
473
- function CloseListener_mousedown({ target }) {
653
+ /**
654
+ * @this {ComponentThis}
655
+ * @param {MouseEvent} e - Mouse event
656
+ */
657
+ function CloseListener_mousedown(e) {
658
+ const target = dom.query.getEventTarget(e);
474
659
  if (
475
660
  this.currentTarget?.contains(target) ||
476
- domUtils.getParentElement(target, '.se-controller') ||
477
- domUtils.hasClass(target, 'se-drag-handle') ||
661
+ dom.query.getParentElement(target, '.se-controller') ||
662
+ dom.utils.hasClass(target, 'se-drag-handle') ||
478
663
  (this.currentPluginName === this.editor.currentControllerName && this.editor.opendControllers.some(({ form }) => form.contains(target)))
479
664
  ) {
480
665
  return;
@@ -482,39 +667,58 @@ function CloseListener_mousedown({ target }) {
482
667
  this.deselect();
483
668
  }
484
669
 
670
+ /**
671
+ * @this {ComponentThis}
672
+ * @param {ClipboardEvent} e - Event object
673
+ */
485
674
  function OnCopy_component(e) {
486
- if (domUtils.isInputElement(e.target) && domUtils.getParentElement(e.target, '.se-modal')) return;
675
+ const target = dom.query.getEventTarget(e);
676
+ if (dom.check.isInputElement(target) && dom.query.getParentElement(target, '.se-modal')) return;
487
677
 
488
678
  const info = this.info;
489
679
  if (!info) return;
490
680
 
491
- SetClipboardComponent(e, info.container, e.clipboardData);
492
- domUtils.addClass(info.container, 'se-copy');
681
+ const cloneContainer = info.container.cloneNode(true);
682
+ dom.utils.removeClass(cloneContainer, 'se-component-selected');
683
+
684
+ if (typeof this.plugins[info.pluginName]?.onCopyComponent !== 'function' || this.plugins[info.pluginName].onCopyComponent({ event: e, cloneContainer, info }) === false) {
685
+ SetClipboardComponent(e, cloneContainer, e.clipboardData);
686
+ }
687
+
493
688
  // copy effect
494
- _w.setTimeout(() => {
495
- domUtils.removeClass(info.container, 'se-copy');
496
- }, 120);
689
+ dom.utils.flashClass(info.container, 'se-copy');
497
690
  }
498
691
 
692
+ /**
693
+ * @this {ComponentThis}
694
+ * @param {ClipboardEvent} e - Event object
695
+ */
499
696
  function OnCut_component(e) {
500
697
  const info = this.info;
501
698
  if (!info) return;
502
699
 
503
- SetClipboardComponent(e, info.container, e.clipboardData);
700
+ const cloneContainer = info.container.cloneNode(true);
701
+ dom.utils.removeClass(cloneContainer, 'se-component-selected');
702
+
703
+ SetClipboardComponent(e, cloneContainer, e.clipboardData);
504
704
  this.deselect();
505
- domUtils.removeItem(info.container);
705
+ dom.utils.removeItem(info.container);
506
706
  }
507
707
 
508
- function OnKeyDown_component(e) {
708
+ /**
709
+ * @this {ComponentThis}
710
+ * @param {KeyboardEvent} e - Event object
711
+ */
712
+ async function OnKeyDown_component(e) {
509
713
  if (this.editor.selectMenuOn) return;
510
714
 
511
- const keyCode = e.keyCode;
512
- const ctrl = e.ctrlKey || e.metaKey || keyCode === 91 || keyCode === 92 || keyCode === 224;
715
+ const keyCode = e.code;
716
+ const ctrl = keyCodeMap.isCtrl(e);
513
717
 
514
718
  // redo, undo
515
719
  if (ctrl) {
516
- if (keyCode !== 17) {
517
- const info = this.editor.shortcutsKeyMap.get(keyCode + (e.shiftKey ? 1000 : 0));
720
+ if (keyCode !== 'ControlRight' && keyCode !== 'ControlLeft') {
721
+ const info = this.editor.shortcutsKeyMap.get(keyCode + (e.shiftKey ? '1000' : ''));
518
722
  if (/^(redo|undo)$/.test(info?.c)) {
519
723
  e.preventDefault();
520
724
  e.stopPropagation();
@@ -525,11 +729,11 @@ function OnKeyDown_component(e) {
525
729
  }
526
730
 
527
731
  // backspace key, delete key
528
- if (keyCode === 8 || keyCode === 46) {
732
+ if (keyCodeMap.isRemoveKey(keyCode)) {
529
733
  e.preventDefault();
530
734
  e.stopPropagation();
531
735
  if (typeof this.currentPlugin?.destroy === 'function') {
532
- this.currentPlugin.destroy(this.currentTarget);
736
+ await this.currentPlugin.destroy(this.currentTarget);
533
737
  this.deselect();
534
738
  this.editor.focus();
535
739
  return;
@@ -537,16 +741,16 @@ function OnKeyDown_component(e) {
537
741
  }
538
742
 
539
743
  // enter key
540
- if (keyCode === 13) {
744
+ if (keyCodeMap.isEnter(keyCode)) {
541
745
  e.preventDefault();
542
746
  const compContext = this.currentInfo || this.get(this.currentTarget);
543
747
  const container = compContext.container || compContext.target;
544
748
  const sibling = container.previousElementSibling || container.nextElementSibling;
545
749
  let newEl = null;
546
- if (domUtils.isListCell(container.parentNode)) {
547
- newEl = domUtils.createElement('BR');
750
+ if (dom.check.isListCell(container.parentNode)) {
751
+ newEl = dom.utils.createElement('BR');
548
752
  } else {
549
- newEl = domUtils.createElement(this.format.isLine(sibling) && !this.format.isBlock(sibling) ? sibling.nodeName : this.options.get('defaultLine'), null, '<br>');
753
+ newEl = dom.utils.createElement(this.format.isLine(sibling) ? sibling.nodeName : this.options.get('defaultLine'), null, '<br>');
550
754
  }
551
755
 
552
756
  const pluginName = this.currentPluginName;
@@ -567,21 +771,22 @@ function OnKeyDown_component(e) {
567
771
  let offset = 1;
568
772
  if (isInline) {
569
773
  switch (keyCode) {
570
- case 37: // left
774
+ case 'ArrowLeft': // left
571
775
  el = container.previousSibling;
572
776
  offset = el?.nodeType === 3 ? el.textContent.length : 1;
573
777
  break;
574
- case 39: // right
778
+ case 'ArrowRight': // right
575
779
  el = container.nextSibling;
780
+ offset = 0;
576
781
  break;
577
- case 38: {
782
+ case 'ArrowUp': {
578
783
  // up
579
784
  const line = this.format.getLine(container, null);
580
785
  el = line?.previousElementSibling;
581
786
  offset = 0;
582
787
  break;
583
788
  }
584
- case 40: {
789
+ case 'ArrowDown': {
585
790
  // down
586
791
  const line = this.format.getLine(container, null);
587
792
  el = line?.nextElementSibling;
@@ -607,16 +812,21 @@ function OnKeyDown_component(e) {
607
812
  e.preventDefault();
608
813
  this.select(elComp.target, elComp.pluginName);
609
814
  } else {
610
- e.stopPropagation();
611
- e.preventDefault();
612
- this.selection.setRange(el, offset, el, offset);
815
+ try {
816
+ this.editor._preventBlur = true;
817
+ e.stopPropagation();
818
+ e.preventDefault();
819
+ this.selection.setRange(el, offset, el, offset);
820
+ } finally {
821
+ this.editor._preventBlur = false;
822
+ }
613
823
  }
614
824
 
615
825
  return;
616
826
  }
617
827
 
618
828
  // ESC
619
- if (keyCode === 27) {
829
+ if (keyCodeMap.isEsc(keyCode)) {
620
830
  this.deselect();
621
831
  return;
622
832
  }
@@ -625,10 +835,8 @@ function OnKeyDown_component(e) {
625
835
  function SetClipboardComponent(e, container, clipboardData) {
626
836
  e.preventDefault();
627
837
  e.stopPropagation();
628
- const pasteContainer = container.cloneNode(true);
629
- domUtils.removeClass(pasteContainer, 'se-component-selected');
630
- pasteContainer.querySelectorAll('.se-figure-selected').forEach((el) => domUtils.removeClass(el, 'se-figure-selected'));
631
- clipboardData.setData('text/html', pasteContainer.outerHTML);
838
+ container.querySelectorAll('.se-figure-selected').forEach((el) => dom.utils.removeClass(el, 'se-figure-selected'));
839
+ clipboardData.setData('text/html', container.outerHTML);
632
840
  }
633
841
 
634
842
  export default Component;