suneditor 3.0.0-beta.9 → 3.0.0-rc.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 (380) hide show
  1. package/README.md +65 -57
  2. package/dist/suneditor-contents.min.css +1 -0
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +110 -61
  6. package/src/assets/design/color.css +36 -17
  7. package/src/assets/design/size.css +2 -0
  8. package/src/assets/icons/defaultIcons.js +17 -2
  9. package/src/assets/suneditor-contents.css +51 -16
  10. package/src/assets/suneditor.css +116 -43
  11. package/src/core/config/contextProvider.js +288 -0
  12. package/src/core/config/eventManager.js +188 -0
  13. package/src/core/config/instanceCheck.js +59 -0
  14. package/src/core/config/optionProvider.js +452 -0
  15. package/src/core/editor.js +166 -1637
  16. package/src/core/event/actions/index.js +229 -0
  17. package/src/core/event/effects/common.registry.js +74 -0
  18. package/src/core/event/effects/keydown.registry.js +573 -0
  19. package/src/core/event/effects/ruleHelpers.js +148 -0
  20. package/src/core/event/eventOrchestrator.js +944 -0
  21. package/src/core/event/executor.js +27 -0
  22. package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +27 -28
  23. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +10 -8
  24. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +22 -23
  25. package/src/core/event/handlers/handler_ww_input.js +75 -0
  26. package/src/core/event/handlers/handler_ww_key.js +228 -0
  27. package/src/core/event/handlers/handler_ww_mouse.js +166 -0
  28. package/src/core/event/ports.js +211 -0
  29. package/src/core/event/reducers/keydown.reducer.js +97 -0
  30. package/src/core/event/rules/keydown.rule.arrow.js +63 -0
  31. package/src/core/event/rules/keydown.rule.backspace.js +208 -0
  32. package/src/core/event/rules/keydown.rule.delete.js +132 -0
  33. package/src/core/event/rules/keydown.rule.enter.js +150 -0
  34. package/src/core/event/rules/keydown.rule.tab.js +35 -0
  35. package/src/core/event/support/defaultLineManager.js +136 -0
  36. package/src/core/event/support/selectionState.js +204 -0
  37. package/src/core/kernel/coreKernel.js +320 -0
  38. package/src/core/kernel/kernelInjector.js +19 -0
  39. package/src/core/kernel/store.js +173 -0
  40. package/src/core/{class → logic/dom}/char.js +42 -45
  41. package/src/core/logic/dom/format.js +1075 -0
  42. package/src/core/{class → logic/dom}/html.js +743 -624
  43. package/src/core/logic/dom/inline.js +1847 -0
  44. package/src/core/logic/dom/listFormat.js +601 -0
  45. package/src/core/{class → logic/dom}/nodeTransform.js +92 -72
  46. package/src/core/{class → logic/dom}/offset.js +254 -317
  47. package/src/core/logic/dom/selection.js +754 -0
  48. package/src/core/logic/panel/menu.js +389 -0
  49. package/src/core/logic/panel/toolbar.js +449 -0
  50. package/src/core/logic/panel/viewer.js +761 -0
  51. package/src/core/logic/shell/_commandExecutor.js +380 -0
  52. package/src/core/logic/shell/commandDispatcher.js +241 -0
  53. package/src/core/logic/shell/component.js +970 -0
  54. package/src/core/logic/shell/focusManager.js +110 -0
  55. package/src/core/{base → logic/shell}/history.js +110 -60
  56. package/src/core/logic/shell/pluginManager.js +363 -0
  57. package/src/core/logic/shell/shortcuts.js +130 -0
  58. package/src/core/logic/shell/ui.js +904 -0
  59. package/src/core/schema/context.js +66 -0
  60. package/src/core/schema/frameContext.js +160 -0
  61. package/src/core/schema/options.js +628 -0
  62. package/src/core/section/constructor.js +194 -500
  63. package/src/core/section/documentType.js +297 -222
  64. package/src/events.js +808 -543
  65. package/src/helper/clipboard.js +27 -16
  66. package/src/helper/converter.js +100 -78
  67. package/src/helper/dom/domCheck.js +56 -30
  68. package/src/helper/dom/domQuery.js +159 -89
  69. package/src/helper/dom/domUtils.js +114 -49
  70. package/src/helper/dom/index.js +5 -1
  71. package/src/helper/env.js +26 -26
  72. package/src/helper/index.js +1 -1
  73. package/src/helper/keyCodeMap.js +25 -28
  74. package/src/helper/numbers.js +4 -8
  75. package/src/helper/unicode.js +4 -8
  76. package/src/hooks/base.js +307 -0
  77. package/src/hooks/params.js +130 -0
  78. package/src/interfaces/contracts.js +227 -0
  79. package/src/interfaces/index.js +7 -0
  80. package/src/interfaces/plugins.js +239 -0
  81. package/src/langs/ckb.js +4 -4
  82. package/src/langs/cs.js +4 -4
  83. package/src/langs/da.js +4 -4
  84. package/src/langs/de.js +4 -4
  85. package/src/langs/en.js +4 -4
  86. package/src/langs/es.js +4 -4
  87. package/src/langs/fa.js +4 -4
  88. package/src/langs/fr.js +4 -4
  89. package/src/langs/he.js +4 -4
  90. package/src/langs/hu.js +4 -4
  91. package/src/langs/it.js +4 -4
  92. package/src/langs/ja.js +4 -4
  93. package/src/langs/km.js +4 -4
  94. package/src/langs/ko.js +4 -4
  95. package/src/langs/lv.js +4 -4
  96. package/src/langs/nl.js +4 -4
  97. package/src/langs/pl.js +4 -4
  98. package/src/langs/pt_br.js +13 -13
  99. package/src/langs/ro.js +4 -4
  100. package/src/langs/ru.js +4 -4
  101. package/src/langs/se.js +4 -4
  102. package/src/langs/tr.js +4 -4
  103. package/src/langs/uk.js +4 -4
  104. package/src/langs/ur.js +4 -4
  105. package/src/langs/zh_cn.js +4 -4
  106. package/src/modules/{Browser.js → contract/Browser.js} +119 -128
  107. package/src/modules/{ColorPicker.js → contract/ColorPicker.js} +132 -142
  108. package/src/modules/contract/Controller.js +589 -0
  109. package/src/modules/{Figure.js → contract/Figure.js} +591 -411
  110. package/src/modules/{HueSlider.js → contract/HueSlider.js} +125 -86
  111. package/src/modules/contract/Modal.js +357 -0
  112. package/src/modules/contract/index.js +9 -0
  113. package/src/modules/manager/ApiManager.js +197 -0
  114. package/src/modules/{FileManager.js → manager/FileManager.js} +128 -160
  115. package/src/modules/manager/index.js +5 -0
  116. package/src/modules/{ModalAnchorEditor.js → ui/ModalAnchorEditor.js} +108 -138
  117. package/src/modules/{SelectMenu.js → ui/SelectMenu.js} +119 -120
  118. package/src/modules/{_DragHandle.js → ui/_DragHandle.js} +1 -1
  119. package/src/modules/ui/index.js +6 -0
  120. package/src/plugins/browser/audioGallery.js +23 -26
  121. package/src/plugins/browser/fileBrowser.js +25 -28
  122. package/src/plugins/browser/fileGallery.js +20 -23
  123. package/src/plugins/browser/imageGallery.js +24 -23
  124. package/src/plugins/browser/videoGallery.js +27 -29
  125. package/src/plugins/command/blockquote.js +11 -17
  126. package/src/plugins/command/exportPDF.js +26 -26
  127. package/src/plugins/command/fileUpload.js +138 -133
  128. package/src/plugins/command/list_bulleted.js +48 -44
  129. package/src/plugins/command/list_numbered.js +48 -44
  130. package/src/plugins/dropdown/align.js +64 -50
  131. package/src/plugins/dropdown/backgroundColor.js +34 -35
  132. package/src/plugins/dropdown/{formatBlock.js → blockStyle.js} +43 -37
  133. package/src/plugins/dropdown/font.js +50 -36
  134. package/src/plugins/dropdown/fontColor.js +34 -35
  135. package/src/plugins/dropdown/hr.js +55 -50
  136. package/src/plugins/dropdown/layout.js +20 -15
  137. package/src/plugins/dropdown/lineHeight.js +46 -30
  138. package/src/plugins/dropdown/list.js +32 -33
  139. package/src/plugins/dropdown/paragraphStyle.js +40 -34
  140. package/src/plugins/dropdown/table/index.js +915 -0
  141. package/src/plugins/dropdown/table/render/table.html.js +308 -0
  142. package/src/plugins/dropdown/table/render/table.menu.js +121 -0
  143. package/src/plugins/dropdown/table/services/table.cell.js +465 -0
  144. package/src/plugins/dropdown/table/services/table.clipboard.js +414 -0
  145. package/src/plugins/dropdown/table/services/table.grid.js +504 -0
  146. package/src/plugins/dropdown/table/services/table.resize.js +463 -0
  147. package/src/plugins/dropdown/table/services/table.selection.js +466 -0
  148. package/src/plugins/dropdown/table/services/table.style.js +844 -0
  149. package/src/plugins/dropdown/table/shared/table.constants.js +109 -0
  150. package/src/plugins/dropdown/table/shared/table.utils.js +219 -0
  151. package/src/plugins/dropdown/template.js +20 -15
  152. package/src/plugins/dropdown/textStyle.js +28 -22
  153. package/src/plugins/field/mention.js +54 -49
  154. package/src/plugins/index.js +5 -5
  155. package/src/plugins/input/fontSize.js +100 -97
  156. package/src/plugins/input/pageNavigator.js +13 -10
  157. package/src/plugins/modal/audio.js +208 -219
  158. package/src/plugins/modal/drawing.js +99 -104
  159. package/src/plugins/modal/embed.js +323 -312
  160. package/src/plugins/modal/image/index.js +942 -0
  161. package/src/plugins/modal/image/render/image.html.js +150 -0
  162. package/src/plugins/modal/image/services/image.size.js +198 -0
  163. package/src/plugins/modal/image/services/image.upload.js +216 -0
  164. package/src/plugins/modal/image/shared/image.constants.js +20 -0
  165. package/src/plugins/modal/link.js +74 -54
  166. package/src/plugins/modal/math.js +126 -119
  167. package/src/plugins/modal/video/index.js +858 -0
  168. package/src/plugins/modal/video/render/video.html.js +131 -0
  169. package/src/plugins/modal/video/services/video.size.js +281 -0
  170. package/src/plugins/modal/video/services/video.upload.js +92 -0
  171. package/src/plugins/popup/anchor.js +57 -49
  172. package/src/suneditor.js +73 -61
  173. package/src/themes/cobalt.css +155 -0
  174. package/src/themes/dark.css +143 -120
  175. package/src/typedef.js +214 -63
  176. package/types/assets/icons/defaultIcons.d.ts +8 -0
  177. package/types/assets/suneditor-contents.css.d.ts +1 -0
  178. package/types/assets/suneditor.css.d.ts +1 -0
  179. package/types/core/config/contextProvider.d.ts +148 -0
  180. package/types/core/config/eventManager.d.ts +68 -0
  181. package/types/core/config/instanceCheck.d.ts +33 -0
  182. package/types/core/config/optionProvider.d.ts +147 -0
  183. package/types/core/editor.d.ts +27 -586
  184. package/types/core/event/actions/index.d.ts +50 -0
  185. package/types/core/event/effects/common.registry.d.ts +56 -0
  186. package/types/core/event/effects/keydown.registry.d.ts +80 -0
  187. package/types/core/event/effects/ruleHelpers.d.ts +36 -0
  188. package/types/core/event/eventOrchestrator.d.ts +191 -0
  189. package/types/core/event/executor.d.ts +13 -0
  190. package/types/core/event/handlers/handler_toolbar.d.ts +38 -0
  191. package/types/core/event/handlers/handler_ww_clipboard.d.ts +36 -0
  192. package/types/core/event/handlers/handler_ww_dragDrop.d.ts +26 -0
  193. package/types/core/event/handlers/handler_ww_input.d.ts +38 -0
  194. package/types/core/event/handlers/handler_ww_key.d.ts +40 -0
  195. package/types/core/event/handlers/handler_ww_mouse.d.ts +47 -0
  196. package/types/core/event/ports.d.ts +256 -0
  197. package/types/core/event/reducers/keydown.reducer.d.ts +84 -0
  198. package/types/core/event/rules/keydown.rule.arrow.d.ts +19 -0
  199. package/types/core/event/rules/keydown.rule.backspace.d.ts +18 -0
  200. package/types/core/event/rules/keydown.rule.delete.d.ts +18 -0
  201. package/types/core/event/rules/keydown.rule.enter.d.ts +18 -0
  202. package/types/core/event/rules/keydown.rule.tab.d.ts +18 -0
  203. package/types/core/event/support/defaultLineManager.d.ts +22 -0
  204. package/types/core/event/support/selectionState.d.ts +29 -0
  205. package/types/core/kernel/coreKernel.d.ts +219 -0
  206. package/types/core/kernel/kernelInjector.d.ts +16 -0
  207. package/types/core/kernel/store.d.ts +170 -0
  208. package/types/core/logic/dom/char.d.ts +46 -0
  209. package/types/core/logic/dom/format.d.ts +234 -0
  210. package/types/core/logic/dom/html.d.ts +290 -0
  211. package/types/core/logic/dom/inline.d.ts +93 -0
  212. package/types/core/logic/dom/listFormat.d.ts +101 -0
  213. package/types/core/logic/dom/nodeTransform.d.ts +110 -0
  214. package/types/core/logic/dom/offset.d.ts +335 -0
  215. package/types/core/logic/dom/selection.d.ts +165 -0
  216. package/types/core/logic/panel/menu.d.ts +93 -0
  217. package/types/core/logic/panel/toolbar.d.ts +128 -0
  218. package/types/core/logic/panel/viewer.d.ts +89 -0
  219. package/types/core/logic/shell/_commandExecutor.d.ts +18 -0
  220. package/types/core/logic/shell/commandDispatcher.d.ts +65 -0
  221. package/types/core/logic/shell/component.d.ts +182 -0
  222. package/types/core/logic/shell/focusManager.d.ts +31 -0
  223. package/types/core/{base → logic/shell}/history.d.ts +13 -12
  224. package/types/core/logic/shell/pluginManager.d.ts +115 -0
  225. package/types/core/logic/shell/shortcuts.d.ts +131 -0
  226. package/types/core/logic/shell/ui.d.ts +261 -0
  227. package/types/core/schema/context.d.ts +104 -0
  228. package/types/core/schema/frameContext.d.ts +320 -0
  229. package/types/core/schema/options.d.ts +1241 -0
  230. package/types/core/section/constructor.d.ts +117 -652
  231. package/types/core/section/documentType.d.ts +43 -61
  232. package/types/events.d.ts +796 -65
  233. package/types/helper/clipboard.d.ts +5 -4
  234. package/types/helper/converter.d.ts +55 -43
  235. package/types/helper/dom/domCheck.d.ts +27 -19
  236. package/types/helper/dom/domQuery.d.ts +76 -57
  237. package/types/helper/dom/domUtils.d.ts +62 -39
  238. package/types/helper/dom/index.d.ts +87 -1
  239. package/types/helper/env.d.ts +16 -13
  240. package/types/helper/index.d.ts +8 -2
  241. package/types/helper/keyCodeMap.d.ts +24 -23
  242. package/types/helper/numbers.d.ts +4 -6
  243. package/types/helper/unicode.d.ts +4 -3
  244. package/types/hooks/base.d.ts +239 -0
  245. package/types/hooks/params.d.ts +65 -0
  246. package/types/index.d.ts +20 -117
  247. package/types/interfaces/contracts.d.ts +183 -0
  248. package/types/interfaces/index.d.ts +3 -0
  249. package/types/interfaces/plugins.d.ts +168 -0
  250. package/types/langs/_Lang.d.ts +2 -2
  251. package/types/langs/index.d.ts +2 -2
  252. package/types/modules/contract/Browser.d.ts +262 -0
  253. package/types/modules/contract/ColorPicker.d.ts +99 -0
  254. package/types/modules/contract/Controller.d.ts +204 -0
  255. package/types/modules/contract/Figure.d.ts +529 -0
  256. package/types/modules/{HueSlider.d.ts → contract/HueSlider.d.ts} +39 -28
  257. package/types/modules/contract/Modal.d.ts +62 -0
  258. package/types/modules/contract/index.d.ts +7 -0
  259. package/types/modules/manager/ApiManager.d.ts +106 -0
  260. package/types/modules/manager/FileManager.d.ts +124 -0
  261. package/types/modules/manager/index.d.ts +3 -0
  262. package/types/modules/ui/ModalAnchorEditor.d.ts +152 -0
  263. package/types/modules/ui/SelectMenu.d.ts +107 -0
  264. package/types/modules/{_DragHandle.d.ts → ui/_DragHandle.d.ts} +1 -0
  265. package/types/modules/ui/index.d.ts +4 -0
  266. package/types/plugins/browser/audioGallery.d.ts +33 -41
  267. package/types/plugins/browser/fileBrowser.d.ts +42 -50
  268. package/types/plugins/browser/fileGallery.d.ts +33 -41
  269. package/types/plugins/browser/imageGallery.d.ts +30 -37
  270. package/types/plugins/browser/videoGallery.d.ts +33 -41
  271. package/types/plugins/command/blockquote.d.ts +4 -21
  272. package/types/plugins/command/exportPDF.d.ts +23 -33
  273. package/types/plugins/command/fileUpload.d.ts +80 -100
  274. package/types/plugins/command/list_bulleted.d.ts +9 -35
  275. package/types/plugins/command/list_numbered.d.ts +9 -35
  276. package/types/plugins/dropdown/align.d.ts +23 -46
  277. package/types/plugins/dropdown/backgroundColor.d.ts +35 -53
  278. package/types/plugins/dropdown/blockStyle.d.ts +45 -0
  279. package/types/plugins/dropdown/font.d.ts +18 -41
  280. package/types/plugins/dropdown/fontColor.d.ts +35 -53
  281. package/types/plugins/dropdown/hr.d.ts +26 -52
  282. package/types/plugins/dropdown/layout.d.ts +19 -25
  283. package/types/plugins/dropdown/lineHeight.d.ts +21 -39
  284. package/types/plugins/dropdown/list.d.ts +6 -34
  285. package/types/plugins/dropdown/paragraphStyle.d.ts +34 -45
  286. package/types/plugins/dropdown/table/index.d.ts +158 -0
  287. package/types/plugins/dropdown/table/render/table.html.d.ts +71 -0
  288. package/types/plugins/dropdown/table/render/table.menu.d.ts +59 -0
  289. package/types/plugins/dropdown/table/services/table.cell.d.ts +76 -0
  290. package/types/plugins/dropdown/table/services/table.clipboard.d.ts +26 -0
  291. package/types/plugins/dropdown/table/services/table.grid.d.ts +77 -0
  292. package/types/plugins/dropdown/table/services/table.resize.d.ts +72 -0
  293. package/types/plugins/dropdown/table/services/table.selection.d.ts +59 -0
  294. package/types/plugins/dropdown/table/services/table.style.d.ts +162 -0
  295. package/types/plugins/dropdown/table/shared/table.constants.d.ts +134 -0
  296. package/types/plugins/dropdown/table/shared/table.utils.d.ts +91 -0
  297. package/types/plugins/dropdown/template.d.ts +19 -25
  298. package/types/plugins/dropdown/textStyle.d.ts +23 -30
  299. package/types/plugins/field/mention.d.ts +66 -72
  300. package/types/plugins/index.d.ts +41 -40
  301. package/types/plugins/input/fontSize.d.ts +57 -96
  302. package/types/plugins/input/pageNavigator.d.ts +5 -8
  303. package/types/plugins/modal/audio.d.ts +60 -153
  304. package/types/plugins/modal/drawing.d.ts +16 -118
  305. package/types/plugins/modal/embed.d.ts +46 -166
  306. package/types/plugins/modal/image/index.d.ts +281 -0
  307. package/types/plugins/modal/image/render/image.html.d.ts +45 -0
  308. package/types/plugins/modal/image/services/image.size.d.ts +55 -0
  309. package/types/plugins/modal/image/services/image.upload.d.ts +24 -0
  310. package/types/plugins/modal/image/shared/image.constants.d.ts +17 -0
  311. package/types/plugins/modal/link.d.ts +46 -66
  312. package/types/plugins/modal/math.d.ts +17 -86
  313. package/types/plugins/modal/{video.d.ts → video/index.d.ts} +89 -221
  314. package/types/plugins/modal/video/render/video.html.d.ts +37 -0
  315. package/types/plugins/modal/video/services/video.size.d.ts +74 -0
  316. package/types/plugins/modal/video/services/video.upload.d.ts +19 -0
  317. package/types/plugins/popup/anchor.d.ts +8 -38
  318. package/types/suneditor.d.ts +55 -24
  319. package/types/typedef.d.ts +344 -228
  320. package/CONTRIBUTING.md +0 -186
  321. package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
  322. package/src/core/base/eventHandlers/handler_ww_mouse.js +0 -194
  323. package/src/core/base/eventManager.js +0 -1523
  324. package/src/core/class/component.js +0 -856
  325. package/src/core/class/format.js +0 -3433
  326. package/src/core/class/menu.js +0 -346
  327. package/src/core/class/selection.js +0 -610
  328. package/src/core/class/shortcuts.js +0 -98
  329. package/src/core/class/toolbar.js +0 -431
  330. package/src/core/class/ui.js +0 -424
  331. package/src/core/class/viewer.js +0 -750
  332. package/src/core/section/actives.js +0 -266
  333. package/src/core/section/context.js +0 -102
  334. package/src/editorInjector/_classes.js +0 -36
  335. package/src/editorInjector/_core.js +0 -87
  336. package/src/editorInjector/index.js +0 -73
  337. package/src/modules/ApiManager.js +0 -191
  338. package/src/modules/Controller.js +0 -474
  339. package/src/modules/Modal.js +0 -346
  340. package/src/modules/index.js +0 -14
  341. package/src/plugins/dropdown/table.js +0 -4034
  342. package/src/plugins/modal/image.js +0 -1376
  343. package/src/plugins/modal/video.js +0 -1226
  344. package/types/core/base/eventHandlers/handler_toolbar.d.ts +0 -41
  345. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +0 -40
  346. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +0 -35
  347. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +0 -45
  348. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +0 -39
  349. package/types/core/base/eventManager.d.ts +0 -401
  350. package/types/core/class/char.d.ts +0 -61
  351. package/types/core/class/component.d.ts +0 -213
  352. package/types/core/class/format.d.ts +0 -623
  353. package/types/core/class/html.d.ts +0 -430
  354. package/types/core/class/menu.d.ts +0 -126
  355. package/types/core/class/nodeTransform.d.ts +0 -93
  356. package/types/core/class/offset.d.ts +0 -522
  357. package/types/core/class/selection.d.ts +0 -188
  358. package/types/core/class/shortcuts.d.ts +0 -142
  359. package/types/core/class/toolbar.d.ts +0 -189
  360. package/types/core/class/ui.d.ts +0 -164
  361. package/types/core/class/viewer.d.ts +0 -140
  362. package/types/core/section/actives.d.ts +0 -46
  363. package/types/core/section/context.d.ts +0 -45
  364. package/types/editorInjector/_classes.d.ts +0 -41
  365. package/types/editorInjector/_core.d.ts +0 -87
  366. package/types/editorInjector/index.d.ts +0 -69
  367. package/types/modules/ApiManager.d.ts +0 -125
  368. package/types/modules/Browser.d.ts +0 -326
  369. package/types/modules/ColorPicker.d.ts +0 -135
  370. package/types/modules/Controller.d.ts +0 -251
  371. package/types/modules/Figure.d.ts +0 -517
  372. package/types/modules/FileManager.d.ts +0 -202
  373. package/types/modules/Modal.d.ts +0 -111
  374. package/types/modules/ModalAnchorEditor.d.ts +0 -236
  375. package/types/modules/SelectMenu.d.ts +0 -194
  376. package/types/modules/index.d.ts +0 -26
  377. package/types/plugins/dropdown/formatBlock.d.ts +0 -55
  378. package/types/plugins/dropdown/table.d.ts +0 -627
  379. package/types/plugins/modal/image.d.ts +0 -451
  380. /package/{LICENSE → LICENSE.txt} +0 -0
@@ -1,176 +1,205 @@
1
- /**
2
- * @fileoverview Char class
3
- */
4
-
5
- import CoreInjector from '../../editorInjector/_core';
6
- import { dom, converter, numbers, unicode, clipboard } from '../../helper';
1
+ import { dom, converter, numbers, unicode, clipboard, env } from '../../../helper';
7
2
 
3
+ const { _d } = env;
8
4
  const REQUIRED_DATA_ATTRS = 'data-se-[^\\s]+';
9
5
  const V2_MIG_DATA_ATTRS = '|data-index|data-file-size|data-file-name|data-exp|data-font-size';
10
6
 
11
7
  /**
12
- * @typedef {Omit<HTML & Partial<__se__EditorInjector>, 'html'>} HTMLThis
13
- */
14
-
15
- /**
16
- * @constructor
17
- * @this {HTMLThis}
18
8
  * @description All HTML related classes involved in the editing area
19
- * @param {__se__EditorCore} editor - The root editor instance
20
9
  */
21
- function HTML(editor) {
22
- CoreInjector.call(this, editor);
23
- const options = this.options;
24
-
25
- // members
26
- this.fontSizeUnitRegExp = null;
27
-
28
- this._isAllowedClassName = function (v) {
29
- return this.test(v) ? v : '';
30
- }.bind(options.get('allowedClassName'));
31
- this._allowHTMLComment = null;
32
- this._disallowedStyleNodesRegExp = null;
33
- this._htmlCheckWhitelistRegExp = null;
34
- this._htmlCheckBlacklistRegExp = null;
35
- this._elementWhitelistRegExp = null;
36
- this._elementBlacklistRegExp = null;
10
+ class HTML {
11
+ #$;
12
+ #store;
13
+
14
+ #frameContext;
15
+ #frameOptions;
16
+ #options;
17
+ #instanceCheck;
18
+
19
+ #fontSizeUnitRegExp;
20
+ #isAllowedClassName;
21
+ #allowHTMLComment;
22
+ #disallowedStyleNodesRegExp;
23
+ #htmlCheckWhitelistRegExp;
24
+ #htmlCheckBlacklistRegExp;
25
+ #elementWhitelistRegExp;
26
+ #elementBlacklistRegExp;
27
+ #attributeWhitelistRegExp;
28
+ #attributeBlacklistRegExp;
29
+ #cleanStyleTagKeyRegExp;
30
+ #cleanStyleRegExpMap;
31
+ #textStyleTags;
32
+ #disallowedTagsRegExp;
33
+ #disallowedTagNameRegExp;
34
+ #allowedTagNameRegExp;
35
+
37
36
  /** @type {Object<string, RegExp>} */
38
- this._attributeWhitelist = null;
37
+ #attributeWhitelist;
39
38
  /** @type {Object<string, RegExp>} */
40
- this._attributeBlacklist = null;
41
- this._attributeWhitelistRegExp = null;
42
- this._attributeBlacklistRegExp = null;
43
- this._cleanStyleTagKeyRegExp = null;
44
- this._cleanStyleRegExpMap = null;
45
- this._textStyleTags = options.get('_textStyleTags');
39
+ #attributeBlacklist;
46
40
  /** @type {Object<string, *>} */
47
- this._autoStyleify = null;
48
- this.__disallowedTagsRegExp = null;
49
- this.__disallowedTagNameRegExp = null;
50
- this.__allowedTagNameRegExp = null;
51
-
52
- // clean styles
53
- const tagStyles = options.get('tagStyles');
54
- const splitTagStyles = {};
55
- for (const k in tagStyles) {
56
- const s = k.split('|');
57
- for (let i = 0, len = s.length, n; i < len; i++) {
58
- n = s[i];
59
- if (!splitTagStyles[n]) splitTagStyles[n] = '';
60
- else splitTagStyles[n] += '|';
61
- splitTagStyles[n] += tagStyles[k];
41
+ #autoStyleify;
42
+
43
+ /**
44
+ * @constructor
45
+ * @param {SunEditor.Kernel} kernel
46
+ */
47
+ constructor(kernel) {
48
+ this.#$ = kernel.$;
49
+ this.#store = kernel.store;
50
+
51
+ const options = (this.#options = this.#$.options);
52
+ this.#frameOptions = this.#$.frameOptions;
53
+ this.#frameContext = this.#$.frameContext;
54
+ this.#instanceCheck = this.#$.instanceCheck;
55
+
56
+ // members
57
+ this.#isAllowedClassName = function (v) {
58
+ return this.test(v) ? v : '';
59
+ }.bind(options.get('allowedClassName'));
60
+
61
+ this.#textStyleTags = options.get('_textStyleTags');
62
+
63
+ // clean styles
64
+ const tagStyles = options.get('tagStyles');
65
+ const splitTagStyles = {};
66
+ for (const k in tagStyles) {
67
+ const s = k.split('|');
68
+ for (let i = 0, len = s.length, n; i < len; i++) {
69
+ n = s[i];
70
+ if (!splitTagStyles[n]) splitTagStyles[n] = '';
71
+ else splitTagStyles[n] += '|';
72
+ splitTagStyles[n] += tagStyles[k];
73
+ }
74
+ }
75
+ for (const k in splitTagStyles) {
76
+ splitTagStyles[k] = new RegExp(`\\s*[^-a-zA-Z](${splitTagStyles[k]})\\s*:[^;]+(?!;)*`, 'gi');
62
77
  }
63
- }
64
- for (const k in splitTagStyles) {
65
- splitTagStyles[k] = new RegExp(`\\s*[^-a-zA-Z](${splitTagStyles[k]})\\s*:[^;]+(?!;)*`, 'gi');
66
- }
67
78
 
68
- const stylesMap = new Map();
69
- const stylesObj = {
70
- ...splitTagStyles,
71
- line: options.get('_lineStylesRegExp')
72
- };
73
- this._textStyleTags.forEach((v) => {
74
- stylesObj[v] = options.get('_textStylesRegExp');
75
- });
76
-
77
- for (const key in stylesObj) {
78
- stylesMap.set(new RegExp(`^(${key})$`), stylesObj[key]);
79
- }
80
- this._cleanStyleTagKeyRegExp = new RegExp(`^(${Object.keys(stylesObj).join('|')})$`, 'i');
81
- this._cleanStyleRegExpMap = stylesMap;
82
-
83
- // font size unit
84
- this.fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
85
-
86
- // extra tags
87
- const allowedExtraTags = options.get('_allowedExtraTag');
88
- const disallowedExtraTags = options.get('_disallowedExtraTag');
89
- this.__disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
90
- this.__disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
91
- this.__allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
92
-
93
- // set disallow text nodes
94
- const disallowStyleNodes = Object.keys(options.get('_defaultStyleTagMap'));
95
- const allowStyleNodes = !options.get('elementWhitelist')
96
- ? []
97
- : options
98
- .get('elementWhitelist')
99
- .split('|')
100
- .filter((v) => /b|i|ins|s|strike/i.test(v));
101
- for (let i = 0; i < allowStyleNodes.length; i++) {
102
- disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
103
- }
104
- this._disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
105
-
106
- // whitelist
107
- // tags
108
- const defaultAttr = options.get('__defaultAttributeWhitelist');
109
- this._allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
110
- // html check
111
- this._htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
112
- this._htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
113
- // elements
114
- this._elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
115
- this._elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
116
- // attributes
117
- const regEndStr = '\\s*=\\s*(")[^"]*\\1';
118
- const _wAttr = options.get('attributeWhitelist');
79
+ const stylesMap = new Map();
80
+ const stylesObj = {
81
+ ...splitTagStyles,
82
+ line: options.get('_lineStylesRegExp'),
83
+ };
84
+ this.#textStyleTags.forEach((v) => {
85
+ stylesObj[v] = options.get('_textStylesRegExp');
86
+ });
119
87
 
120
- /** @type {Object<string, RegExp>} */
121
- let tagsAttr = {};
122
- let allAttr = '';
123
- if (_wAttr) {
124
- for (const k in _wAttr) {
125
- if (/^on[a-z]+$/i.test(_wAttr[k])) continue;
126
- if (k === '*') {
127
- allAttr = GetRegList(_wAttr[k], defaultAttr);
128
- } else {
129
- tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_wAttr[k], '') + ')' + regEndStr, 'ig');
88
+ for (const key in stylesObj) {
89
+ stylesMap.set(new RegExp(`^(${key})$`), stylesObj[key]);
90
+ }
91
+ this.#cleanStyleTagKeyRegExp = new RegExp(`^(${Object.keys(stylesObj).join('|')})$`, 'i');
92
+ this.#cleanStyleRegExpMap = stylesMap;
93
+
94
+ // font size unit
95
+ this.#fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
96
+
97
+ // extra tags
98
+ const allowedExtraTags = options.get('_allowedExtraTag');
99
+ const disallowedExtraTags = options.get('_disallowedExtraTag');
100
+ this.#disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
101
+ this.#disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
102
+ this.#allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
103
+
104
+ // set disallow text nodes
105
+ const disallowStyleNodes = Object.keys(options.get('_defaultStyleTagMap'));
106
+ const allowStyleNodes = !options.get('elementWhitelist')
107
+ ? []
108
+ : options
109
+ .get('elementWhitelist')
110
+ .split('|')
111
+ .filter((v) => /b|i|ins|s|strike/i.test(v));
112
+ for (let i = 0; i < allowStyleNodes.length; i++) {
113
+ disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
114
+ }
115
+ this.#disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
116
+
117
+ // whitelist
118
+ // tags
119
+ const defaultAttr = options.get('__defaultAttributeWhitelist');
120
+ this.#allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
121
+ // html check
122
+ this.#htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
123
+ this.#htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
124
+ // elements
125
+ this.#elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
126
+ this.#elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
127
+ // attributes
128
+ const regEndStr = '\\s*=\\s*(")[^"]*\\1';
129
+ const _wAttr = options.get('attributeWhitelist');
130
+
131
+ /** @type {Object<string, RegExp>} */
132
+ let tagsAttr = {};
133
+ let allAttr = '';
134
+ if (_wAttr) {
135
+ for (const k in _wAttr) {
136
+ if (/^on[a-z]+$/i.test(_wAttr[k])) continue;
137
+ if (k === '*') {
138
+ allAttr = GetRegList(_wAttr[k], defaultAttr);
139
+ } else {
140
+ tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_wAttr[k], defaultAttr) + ')' + regEndStr, 'ig');
141
+ }
130
142
  }
131
143
  }
132
- }
133
144
 
134
- this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
135
- this._attributeWhitelist = tagsAttr;
136
-
137
- // blacklist
138
- const _bAttr = options.get('attributeBlacklist');
139
- tagsAttr = {};
140
- allAttr = '';
141
- if (_bAttr) {
142
- for (const k in _bAttr) {
143
- if (k === '*') {
144
- allAttr = GetRegList(_bAttr[k], '');
145
- } else {
146
- tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_bAttr[k], '') + ')' + regEndStr, 'ig');
145
+ this.#attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
146
+ this.#attributeWhitelist = tagsAttr;
147
+
148
+ // blacklist
149
+ const _bAttr = options.get('attributeBlacklist');
150
+ tagsAttr = {};
151
+ allAttr = '';
152
+ if (_bAttr) {
153
+ for (const k in _bAttr) {
154
+ if (k === '*') {
155
+ allAttr = GetRegList(_bAttr[k], '');
156
+ } else {
157
+ tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_bAttr[k], '') + ')' + regEndStr, 'ig');
158
+ }
147
159
  }
148
160
  }
149
- }
150
161
 
151
- this._attributeBlacklistRegExp = new RegExp('\\s(?:' + (allAttr || '^') + ')' + regEndStr, 'ig');
152
- this._attributeBlacklist = tagsAttr;
162
+ this.#attributeBlacklistRegExp = new RegExp('\\s(?:' + (allAttr || '^') + ')' + regEndStr, 'ig');
163
+ this.#attributeBlacklist = tagsAttr;
153
164
 
154
- // autoStyleify
155
- this.__resetAutoStyleify(options.get('autoStyleify'));
156
- }
165
+ // autoStyleify
166
+ this.__resetAutoStyleify(options.get('autoStyleify'));
167
+ }
157
168
 
158
- HTML.prototype = {
159
169
  /**
160
- * @this {HTMLThis}
161
170
  * @description Filters an HTML string based on allowed and disallowed tags, with optional custom validation.
162
171
  * - Removes blacklisted tags and keeps only whitelisted tags.
163
172
  * - Allows custom validation functions to replace, modify, or remove elements.
164
173
  * @param {string} html - The HTML string to be filtered.
165
174
  * @param {Object} params - Filtering parameters.
166
- * @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by '|'. (e.g. "div|p|span").
167
- * @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by '|'. (e.g. "script|iframe").
175
+ * @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by `'|'`. (e.g. `"div|p|span"`).
176
+ * @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by `'|'`. (e.g. `"script|iframe"`).
168
177
  * @param {(node: Node) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
169
178
  * - Return `null` to remove the node.
170
179
  * - Return a `Node` to replace the current node.
171
- * - Return a `string` to replace the node's outerHTML.
180
+ * - Return a `string` to replace the node's `outerHTML`.
172
181
  * @param {boolean} [params.validateAll] - Whether to apply validation to all nodes.
173
182
  * @returns {string} - The filtered HTML string.
183
+ * @example
184
+ * // Remove script and iframe tags using blacklist
185
+ * const filtered = editor.html.filter('<div>Content<script>alert("xss")</script></div>', {
186
+ * tagBlacklist: 'script|iframe'
187
+ * });
188
+ *
189
+ * // Keep only specific tags using whitelist
190
+ * const filtered = editor.html.filter('<div><span>Text</span><img src="x"></div>', {
191
+ * tagWhitelist: 'div|span'
192
+ * });
193
+ *
194
+ * // Custom validation to modify nodes
195
+ * const filtered = editor.html.filter('<div class="test"><a href="#">Link</a></div>', {
196
+ * validate: (node) => {
197
+ * if (node.tagName === 'A') {
198
+ * node.setAttribute('target', '_blank');
199
+ * return node;
200
+ * }
201
+ * }
202
+ * });
174
203
  */
175
204
  filter(html, { tagWhitelist, tagBlacklist, validate, validateAll }) {
176
205
  if (tagWhitelist) {
@@ -182,28 +211,11 @@ HTML.prototype = {
182
211
  if (validate) {
183
212
  const parseDocument = new DOMParser().parseFromString(html, 'text/html');
184
213
  parseDocument.body.querySelectorAll('*').forEach((node) => {
185
- if (!node.closest('.se-component') && !node.closest('.se-flex-component')) {
186
- const result = validate(node);
187
- if (result === null) {
188
- node.remove();
189
- } else if (result instanceof Node) {
190
- node.replaceWith(result);
191
- } else if (typeof result === 'string') {
192
- node.outerHTML = result;
193
- }
194
- }
195
- });
196
- html = parseDocument.body.innerHTML;
197
- } else if (validateAll) {
198
- const parseDocument = new DOMParser().parseFromString(html, 'text/html');
199
- const compClass = ['.se-component', '.se-flex-component'];
200
- const closestAny = (element) => compClass.some((selector) => element.closest(selector));
201
- parseDocument.body.querySelectorAll('*').forEach((node) => {
202
- if (!closestAny(node)) {
214
+ if (validateAll || (!node.closest('.se-component') && !node.closest('.se-flex-component'))) {
203
215
  const result = validate(node);
204
216
  if (result === null) {
205
217
  node.remove();
206
- } else if (result instanceof Node) {
218
+ } else if (this.#instanceCheck.isNode(result)) {
207
219
  node.replaceWith(result);
208
220
  } else if (typeof result === 'string') {
209
221
  node.outerHTML = result;
@@ -214,48 +226,58 @@ HTML.prototype = {
214
226
  }
215
227
 
216
228
  return html;
217
- },
229
+ }
218
230
 
219
231
  /**
220
- * @this {HTMLThis}
221
232
  * @description Cleans and compresses HTML code to suit the editor format.
222
233
  * @param {string} html HTML string to clean and compress
223
234
  * @param {Object} [options] Cleaning options
224
- * @param {boolean} [options.forceFormat=false] If true, wraps text nodes without a format node in the format tag.
225
- * @param {string|RegExp|null} [options.whitelist=null] Regular expression of allowed tags.
226
- * Create RegExp object using helper.converter.createElementWhitelist method.
227
- * @param {string|RegExp|null} [options.blacklist=null] Regular expression of disallowed tags.
228
- * Create RegExp object using helper.converter.createElementBlacklist method.
229
- * @param {boolean} [options._freeCodeViewMode=false] If true, the free code view mode is enabled.
235
+ * @param {boolean} [options.forceFormat=false] If `true`, wraps text nodes without a format node in the format tag.
236
+ * @param {?(string|RegExp)} [options.whitelist] Regular expression of allowed tags.
237
+ * Create RegExp object using `helper.converter.createElementWhitelist` method.
238
+ * @param {?(string|RegExp)} [options.blacklist] Regular expression of disallowed tags.
239
+ * Create RegExp object using `helper.converter.createElementBlacklist` method.
240
+ * @param {boolean} [options._freeCodeViewMode=false] If `true`, the free code view mode is enabled.
230
241
  * @returns {string} Cleaned and compressed HTML string
242
+ * @example
243
+ * // Basic cleaning
244
+ * const cleaned = editor.html.clean('<div> <p>Hello</p> </div>');
245
+ *
246
+ * // Clean with format wrapping
247
+ * const cleaned = editor.html.clean('Plain text content', { forceFormat: true });
248
+ *
249
+ * // Clean with blacklist to remove specific tags
250
+ * const cleaned = editor.html.clean('<div><script>alert(1)</script>Content</div>', {
251
+ * blacklist: 'script|style'
252
+ * });
231
253
  */
232
254
  clean(html, { forceFormat, whitelist, blacklist, _freeCodeViewMode } = {}) {
233
- const { tagFilter, formatFilter, classFilter, styleNodeFilter, attrFilter, styleFilter } = this.options.get('strictMode');
255
+ const { tagFilter, formatFilter, classFilter, textStyleTagFilter, attrFilter, styleFilter } = this.#options.get('strictMode');
234
256
  let cleanData = '';
235
257
 
236
258
  html = this.compress(html);
237
259
 
238
260
  if (tagFilter) {
239
- html = html.replace(this.__disallowedTagsRegExp, '');
240
- html = this._deleteDisallowedTags(html, this._elementWhitelistRegExp, this._elementBlacklistRegExp).replace(/<br\/?>$/i, '');
261
+ html = html.replace(this.#disallowedTagsRegExp, '');
262
+ html = this.#deleteDisallowedTags(html, this.#elementWhitelistRegExp, this.#elementBlacklistRegExp).replace(/<br\/?>$/i, '');
241
263
  }
242
264
 
243
- if (this._autoStyleify) {
265
+ if (this.#autoStyleify) {
244
266
  const domParser = new DOMParser().parseFromString(html, 'text/html');
245
- dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this._autoStyleify));
267
+ dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this.#autoStyleify), null);
246
268
  html = domParser.body.innerHTML;
247
269
  }
248
270
 
249
271
  if (attrFilter || styleFilter) {
250
- html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, CleanElements.bind(this, attrFilter, styleFilter));
272
+ html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, this.#CleanElements.bind(this, attrFilter, styleFilter));
251
273
  }
252
274
 
253
275
  // get dom tree
254
- const domParser = this._d.createRange().createContextualFragment(html);
276
+ const domParser = _d.createRange().createContextualFragment(html);
255
277
 
256
278
  if (tagFilter) {
257
279
  try {
258
- this._consistencyCheckOfHTML(domParser, this._htmlCheckWhitelistRegExp, this._htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
280
+ this.#consistencyCheckOfHTML(domParser, this.#htmlCheckWhitelistRegExp, this.#htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
259
281
  } catch (error) {
260
282
  console.warn('[SUNEDITOR.html.clean.fail]', error.message);
261
283
  }
@@ -269,38 +291,34 @@ HTML.prototype = {
269
291
 
270
292
  const attrs = JSON.parse(iframePlaceholders[i].getAttribute('data-se-iframe-holder-attrs'));
271
293
  for (const [key, value] of Object.entries(attrs)) {
294
+ // Block event handler attributes and validate src protocol
295
+ if (/^on/i.test(key)) continue;
296
+ if (key === 'src' && !_SAFE_URL_PROTOCOL.test(String(value).replace(/[\s\r\n\t]+/g, ''))) continue;
272
297
  iframe.setAttribute(key, value);
273
298
  }
274
299
 
275
300
  iframePlaceholders[i].replaceWith(iframe);
276
301
  }
277
302
 
278
- if (this.options.get('__pluginRetainFilter')) {
279
- this.editor._MELInfo.forEach((method, query) => {
280
- const infoLst = domParser.querySelectorAll(query);
281
- for (let i = 0, len = infoLst.length; i < len; i++) {
282
- method(infoLst[i]);
283
- }
284
- });
285
- }
303
+ this.#$.pluginManager.applyRetainFormat(domParser);
286
304
 
287
305
  if (formatFilter) {
288
306
  let domTree = domParser.childNodes;
289
- if (!forceFormat) forceFormat = this._isFormatData(domTree);
290
- if (forceFormat) domTree = this._editFormat(domParser).childNodes;
307
+ forceFormat ||= this.#isFormatData(domTree);
308
+ if (forceFormat) domTree = this.#editFormat(domParser).childNodes;
291
309
 
292
310
  for (let i = 0, len = domTree.length, t; i < len; i++) {
293
311
  t = domTree[i];
294
- if (this.__allowedTagNameRegExp.test(t.nodeName)) {
312
+ if (this.#allowedTagNameRegExp.test(t.nodeName)) {
295
313
  cleanData += /** @type {HTMLElement} */ (t).outerHTML;
296
314
  continue;
297
315
  }
298
- cleanData += this._makeLine(t, forceFormat);
316
+ cleanData += this.#makeLine(t, forceFormat);
299
317
  }
300
318
  }
301
319
 
302
320
  // set clean data
303
- if (!cleanData) cleanData = html;
321
+ cleanData ||= html;
304
322
 
305
323
  // whitelist, blacklist
306
324
  if (tagFilter) {
@@ -308,46 +326,58 @@ HTML.prototype = {
308
326
  if (blacklist) cleanData = cleanData.replace(typeof blacklist === 'string' ? converter.createElementBlacklist(blacklist) : blacklist, '');
309
327
  }
310
328
 
311
- if (styleNodeFilter) {
312
- cleanData = this._styleNodeConvertor(cleanData);
329
+ if (textStyleTagFilter) {
330
+ cleanData = this.#styleNodeConvertor(cleanData);
313
331
  }
314
332
 
315
333
  return cleanData;
316
- },
334
+ }
317
335
 
318
336
  /**
319
- * @this {HTMLThis}
320
337
  * @description Inserts an (HTML element / HTML string / plain string) at the selection range.
321
- * - If "frameOptions.get('charCounter_max')" is exceeded when "html" is added, null is returned without addition.
338
+ * - If `frameOptions.get('charCounter_max')` is exceeded when `html` is added, `null` is returned without addition.
322
339
  * @param {Node|string} html HTML Element or HTML string or plain string
323
340
  * @param {Object} [options] Options
324
- * @param {boolean} [options.selectInserted=false] If true, selects the range of the inserted node.
325
- * @param {boolean} [options.skipCharCount=false] If true, inserts even if "frameOptions.get('charCounter_max')" is exceeded.
326
- * @param {boolean} [options.skipCleaning=false] If true, inserts the HTML string without refining it with html.clean.
327
- * @returns {HTMLElement|null} The inserted element or null if insertion failed
341
+ * @param {boolean} [options.selectInserted=false] If `true`, selects the range of the inserted node.
342
+ * @param {boolean} [options.skipCharCount=false] If `true`, inserts even if `frameOptions.get('charCounter_max')` is exceeded.
343
+ * @param {boolean} [options.skipCleaning=false] If `true`, inserts the HTML string without refining it with `html.clean`.
344
+ * @returns {HTMLElement|null} The inserted element or `null` if insertion failed
345
+ * @example
346
+ * // Insert HTML string at cursor
347
+ * editor.html.insert('<strong>Bold text</strong>');
348
+ *
349
+ * // Insert and select the inserted content
350
+ * editor.html.insert('<p>New paragraph</p>', { selectInserted: true });
351
+ *
352
+ * // Insert raw HTML without cleaning
353
+ * editor.html.insert('<div class="custom">Content</div>', { skipCleaning: true });
328
354
  */
329
355
  insert(html, { selectInserted, skipCharCount, skipCleaning } = {}) {
330
- if (!this.editor.frameContext.get('wysiwyg').contains(this.selection.get().focusNode)) this.editor.focus();
356
+ if (!this.#frameContext.get('wysiwyg').contains(this.#$.selection.get().focusNode)) this.#$.focusManager.focus();
331
357
 
358
+ this.remove();
359
+ this.#$.focusManager.focus();
360
+
361
+ let focusNode = null;
332
362
  if (typeof html === 'string') {
333
363
  if (!skipCleaning) html = this.clean(html, { forceFormat: false, whitelist: null, blacklist: null });
334
364
  try {
335
- if (dom.check.isListCell(this.format.getLine(this.selection.getNode(), null))) {
336
- const domParser = this._d.createRange().createContextualFragment(html);
365
+ if (dom.check.isListCell(this.#$.format.getLine(this.#$.selection.getNode(), null))) {
366
+ const domParser = _d.createRange().createContextualFragment(html);
337
367
  const domTree = domParser.childNodes;
338
- if (this._isFormatData(domTree)) html = this._convertListCell(domTree);
368
+ if (this.#isFormatData(domTree)) html = this.#convertListCell(domTree);
339
369
  }
340
370
 
341
- const domParser = this._d.createRange().createContextualFragment(html);
371
+ const domParser = _d.createRange().createContextualFragment(html);
342
372
  const domTree = domParser.childNodes;
343
373
 
344
374
  if (!skipCharCount) {
345
- const type = this.editor.frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
375
+ const type = this.#frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
346
376
  let checkHTML = '';
347
377
  for (let i = 0, len = domTree.length; i < len; i++) {
348
378
  checkHTML += domTree[i][type];
349
379
  }
350
- if (!this.char.check(checkHTML)) return;
380
+ if (!this.#$.char.check(checkHTML)) return;
351
381
  }
352
382
 
353
383
  let c, a, t, prev, firstCon;
@@ -359,74 +389,109 @@ HTML.prototype = {
359
389
  }
360
390
  t = this.insertNode(c, { afterNode: a, skipCharCount: true });
361
391
  a = t.container || t;
362
- if (!firstCon) firstCon = t;
392
+ firstCon ||= t;
363
393
  prev = c;
364
394
  }
365
395
 
366
396
  if (prev?.nodeType === 3 && a?.nodeType === 1) a = prev;
367
397
  const offset = a.nodeType === 3 ? t.endOffset || a.textContent.length : a.childNodes.length;
398
+ focusNode = a;
368
399
 
369
400
  if (selectInserted) {
370
- this.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
371
- } else if (!this.component.is(a)) {
372
- this.selection.setRange(a, offset, a, offset);
401
+ this.#$.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
402
+ } else if (!this.#$.component.is(a)) {
403
+ this.#$.selection.setRange(a, offset, a, offset);
373
404
  }
374
405
  } catch (error) {
375
- if (this.editor.frameContext.get('isReadOnly') || this.editor.frameContext.get('isDisabled')) return;
406
+ if (this.#frameContext.get('isReadOnly') || this.#frameContext.get('isDisabled')) return;
376
407
  throw Error(`[SUNEDITOR.html.insert.error] ${error.message}`);
377
408
  }
378
409
  } else {
379
- if (this.component.is(html)) {
380
- this.component.insert(html, { skipCharCount, skipSelection: false, skipHistory: false });
410
+ if (this.#$.component.is(html)) {
411
+ this.#$.component.insert(html, { skipCharCount, insertBehavior: 'none' });
381
412
  } else {
382
413
  let afterNode = null;
383
- if (this.format.isLine(html) || dom.check.isMedia(html)) {
384
- afterNode = this.format.getLine(this.selection.getNode(), null);
414
+ if (this.#$.format.isLine(html) || dom.check.isMedia(html)) {
415
+ afterNode = this.#$.format.getLine(this.#$.selection.getNode(), null);
385
416
  }
386
417
  this.insertNode(html, { afterNode, skipCharCount });
387
418
  }
388
419
  }
389
420
 
390
- this.editor.effectNode = null;
391
- this.editor.focus();
392
- this.history.push(false);
393
- },
421
+ // focus
422
+ this.#store.set('_lastSelectionNode', null);
423
+
424
+ if (focusNode) {
425
+ const children = dom.query.getListChildNodes(focusNode, null, null);
426
+ if (children.length > 0) {
427
+ focusNode = children.at(-1);
428
+ const offset = focusNode?.nodeType === 3 ? focusNode.textContent.length : 1;
429
+ this.#$.selection.setRange(focusNode, offset, focusNode, offset);
430
+ } else {
431
+ this.#$.focusManager.focus();
432
+ }
433
+ } else {
434
+ this.#$.focusManager.focus();
435
+ }
436
+
437
+ this.#$.history.push(false);
438
+ }
394
439
 
395
440
  /**
396
- * @this {HTMLThis}
397
441
  * @description Delete selected node and insert argument value node and return.
398
- * - If the "afterNode" exists, it is inserted after the "afterNode"
399
- * - Inserting a text node merges with both text nodes on both sides and returns a new "{ container, startOffset, endOffset }".
442
+ * - If the `afterNode` exists, it is inserted after the `afterNode`
443
+ * - Inserting a text node merges with both text nodes on both sides and returns a new `{ container, startOffset, endOffset }`.
400
444
  * @param {Node} oNode Node to be inserted
401
445
  * @param {Object} [options] Options
402
446
  * @param {Node} [options.afterNode=null] If the node exists, it is inserted after the node
403
- * @param {boolean} [options.skipCharCount=null] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
447
+ * @param {boolean} [options.skipCharCount=null] If `true`, it will be inserted even if `frameOptions.get('charCounter_max')` is exceeded.
404
448
  * @returns {Object|Node|null}
449
+ * @example
450
+ * // Insert node at current selection
451
+ * const strongNode = document.createElement('strong');
452
+ * strongNode.textContent = 'Bold';
453
+ * editor.html.insertNode(strongNode);
454
+ *
455
+ * // Insert node after a specific element
456
+ * const paragraph = editor.html.getNode();
457
+ * const newSpan = document.createElement('span');
458
+ * editor.html.insertNode(newSpan, { afterNode: paragraph });
459
+ *
460
+ * // Insert bypassing character count limit
461
+ * editor.html.insertNode(largeContentNode, { skipCharCount: true });
405
462
  */
406
463
  insertNode(oNode, { afterNode, skipCharCount } = {}) {
407
464
  let result = null;
408
- if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(oNode))) {
465
+ if (this.#frameContext.get('isReadOnly') || (!skipCharCount && !this.#$.char.check(oNode))) {
409
466
  return result;
410
467
  }
411
468
 
412
469
  let fNode = null;
413
- let range = this.selection.getRange();
414
- let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.format.getLine(this.selection.getNode(), null);
470
+ let range = null;
471
+
472
+ if (afterNode) {
473
+ const afterNewLine = this.#$.format.isLine(afterNode) || this.#$.format.isBlock(afterNode) || this.#$.component.is(afterNode) ? this.#$.format.addLine(afterNode, null) : afterNode;
474
+ range = this.#$.selection.setRange(afterNewLine, 1, afterNewLine, 1);
475
+ } else {
476
+ range = this.#$.selection.getRange();
477
+ }
478
+
479
+ let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.#$.format.getLine(this.#$.selection.getNode(), null);
415
480
  let insertListCell = dom.check.isListCell(line) && (dom.check.isListCell(oNode) || dom.check.isList(oNode));
416
481
 
417
482
  let parentNode,
418
483
  originAfter,
419
484
  tempAfterNode,
420
485
  tempParentNode = null;
421
- const freeFormat = this.format.isBrLine(line);
422
- const isFormats = (!freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) || this.component.isBasic(oNode);
486
+ const freeFormat = this.#$.format.isBrLine(line);
487
+ const isFormats = this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || this.#$.component.isBasic(oNode);
423
488
 
424
489
  if (insertListCell) {
425
490
  tempAfterNode = afterNode || dom.check.isList(oNode) ? line.lastChild : line.nextElementSibling;
426
491
  tempParentNode = dom.check.isList(oNode) ? line : (tempAfterNode || line).parentNode;
427
492
  }
428
493
 
429
- if (!afterNode && (isFormats || this.component.isBasic(oNode) || dom.check.isMedia(oNode))) {
494
+ if (!afterNode && (isFormats || this.#$.component.isBasic(oNode) || dom.check.isMedia(oNode))) {
430
495
  const isEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
431
496
  const r = this.remove();
432
497
  const container = r.container;
@@ -452,11 +517,13 @@ HTML.prototype = {
452
517
  tempAfterNode = null;
453
518
  } else if (container.nodeType === 3 || dom.check.isBreak(container) || insertListCell) {
454
519
  const depthFormat = dom.query.getParentElement(container, (current) => {
455
- return this.format.isBlock(current) || dom.check.isListCell(current);
520
+ return this.#$.format.isBlock(current) || dom.check.isListCell(current);
456
521
  });
457
- afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
522
+ afterNode = this.#$.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
458
523
  if (!afterNode) {
459
- tempAfterNode = afterNode = line;
524
+ if (!dom.check.isListCell(line)) {
525
+ tempAfterNode = afterNode = line;
526
+ }
460
527
  } else if (insertListCell) {
461
528
  if (line.contains(container)) {
462
529
  const subList = dom.check.isList(line.lastElementChild);
@@ -483,11 +550,11 @@ HTML.prototype = {
483
550
  }
484
551
  }
485
552
 
486
- range = !afterNode && !isFormats ? this.selection.getRangeAndAddLine(this.selection.getRange(), null) : this.selection.getRange();
553
+ range = !afterNode && !isFormats ? this.#$.selection.getRangeAndAddLine(this.#$.selection.getRange(), null) : this.#$.selection.getRange();
487
554
  const commonCon = range.commonAncestorContainer;
488
555
  const startOff = range.startOffset;
489
556
  const endOff = range.endOffset;
490
- const formatRange = range.startContainer === commonCon && this.format.isLine(commonCon);
557
+ const formatRange = range.startContainer === commonCon && this.#$.format.isLine(commonCon);
491
558
  const startCon = formatRange ? commonCon.childNodes[startOff] || commonCon.childNodes[0] || range.startContainer : range.startContainer;
492
559
  const endCon = formatRange ? commonCon.childNodes[endOff] || commonCon.childNodes[commonCon.childNodes.length - 1] || range.endContainer : range.endContainer;
493
560
 
@@ -543,10 +610,10 @@ HTML.prototype = {
543
610
  const prevContainer = removedTag.prevContainer;
544
611
 
545
612
  if (container?.childNodes.length === 0 && isFormats) {
546
- if (this.format.isLine(container)) {
613
+ if (this.#$.format.isLine(container)) {
547
614
  container.innerHTML = '<br>';
548
- } else if (this.format.isBlock(container)) {
549
- container.innerHTML = '<' + this.options.get('defaultLine') + '><br></' + this.options.get('defaultLine') + '>';
615
+ } else if (this.#$.format.isBlock(container)) {
616
+ container.innerHTML = '<' + this.#options.get('defaultLine') + '><br></' + this.#options.get('defaultLine') + '>';
550
617
  }
551
618
  }
552
619
 
@@ -566,15 +633,15 @@ HTML.prototype = {
566
633
  } else {
567
634
  afterNode = null;
568
635
  }
569
- } else if (dom.check.isWysiwygFrame(container) && !this.format.isLine(oNode)) {
570
- parentNode = container.appendChild(dom.utils.createElement(this.options.get('defaultLine')));
636
+ } else if (dom.check.isWysiwygFrame(container) && !this.#$.format.isLine(oNode)) {
637
+ parentNode = container.appendChild(dom.utils.createElement(this.#options.get('defaultLine')));
571
638
  afterNode = null;
572
639
  } else {
573
640
  afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
574
641
  parentNode = !afterNode || !afterNode.parentNode ? commonCon : afterNode.parentNode;
575
642
  }
576
643
 
577
- while (afterNode && !this.format.isLine(afterNode) && afterNode.parentNode !== commonCon) {
644
+ while (afterNode && !this.#$.format.isLine(afterNode) && afterNode.parentNode !== commonCon) {
578
645
  afterNode = afterNode.parentNode;
579
646
  }
580
647
  }
@@ -589,24 +656,21 @@ HTML.prototype = {
589
656
 
590
657
  try {
591
658
  // set node
592
- const wysiwyg = this.editor.frameContext.get('wysiwyg');
659
+ const wysiwyg = this.#frameContext.get('wysiwyg');
593
660
  if (!insertListCell) {
594
661
  if (dom.check.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
595
662
  parentNode = wysiwyg;
596
663
  afterNode = null;
597
664
  }
598
665
 
599
- if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.component.isBasic(oNode))) {
666
+ if (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.#$.component.isBasic(oNode))) {
600
667
  const oldParent = parentNode;
601
- if (dom.check.isList(afterNode)) {
602
- parentNode = afterNode;
603
- afterNode = null;
604
- } else if (dom.check.isListCell(afterNode)) {
668
+ if (dom.check.isListCell(afterNode)) {
605
669
  parentNode = afterNode.previousElementSibling || afterNode;
606
670
  } else if (!originAfter && !afterNode) {
607
671
  const r = this.remove();
608
- const container = r.container.nodeType === 3 ? (dom.check.isListCell(this.format.getLine(r.container, null)) ? r.container : this.format.getLine(r.container, null) || r.container.parentNode) : r.container;
609
- const rangeCon = dom.check.isWysiwygFrame(container) || this.format.isBlock(container);
672
+ const container = r.container.nodeType === 3 ? (dom.check.isListCell(this.#$.format.getLine(r.container, null)) ? r.container : this.#$.format.getLine(r.container, null) || r.container.parentNode) : r.container;
673
+ const rangeCon = dom.check.isWysiwygFrame(container) || this.#$.format.isBlock(container);
610
674
  parentNode = rangeCon ? container : container.parentNode;
611
675
  afterNode = rangeCon ? null : container.nextSibling;
612
676
  }
@@ -614,13 +678,13 @@ HTML.prototype = {
614
678
  if (oldParent.childNodes.length === 0 && parentNode !== oldParent) dom.utils.removeItem(oldParent);
615
679
  }
616
680
 
617
- if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
618
- afterNode = parentNode.nextElementSibling;
681
+ if (isFormats && !freeFormat && !this.#$.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
682
+ afterNode = /** @type {HTMLElement} */ (parentNode).nextElementSibling;
619
683
  parentNode = parentNode.parentNode;
620
684
  }
621
685
 
622
686
  if (dom.check.isWysiwygFrame(parentNode) && (oNode.nodeType === 3 || dom.check.isBreak(oNode))) {
623
- const formatNode = dom.utils.createElement(this.options.get('defaultLine'), null, oNode);
687
+ const formatNode = dom.utils.createElement(this.#options.get('defaultLine'), null, oNode);
624
688
  fNode = oNode;
625
689
  oNode = formatNode;
626
690
  }
@@ -652,7 +716,7 @@ HTML.prototype = {
652
716
  insertListCell = true;
653
717
  }
654
718
 
655
- this._checkDuplicateNode(oNode, parentNode);
719
+ this.#checkDuplicateNode(oNode, parentNode);
656
720
  parentNode.insertBefore(oNode, afterNode);
657
721
 
658
722
  if (insertListCell) {
@@ -682,7 +746,7 @@ HTML.prototype = {
682
746
  } finally {
683
747
  if (fNode) oNode = fNode;
684
748
 
685
- const dupleNodes = parentNode.querySelectorAll('[data-duple]');
749
+ const dupleNodes = /** @type {HTMLElement} */ (parentNode).querySelectorAll('[data-duple]');
686
750
  if (dupleNodes.length > 0) {
687
751
  for (let i = 0, len = dupleNodes.length, d, c, ch, parent; i < len; i++) {
688
752
  d = dupleNodes[i];
@@ -699,23 +763,23 @@ HTML.prototype = {
699
763
  }
700
764
  }
701
765
 
702
- if ((this.format.isLine(oNode) || this.component.isBasic(oNode)) && startCon === endCon) {
703
- const cItem = this.format.getLine(commonCon, null);
766
+ if ((this.#$.format.isLine(oNode) || this.#$.component.isBasic(oNode)) && startCon === endCon) {
767
+ const cItem = this.#$.format.getLine(commonCon, null);
704
768
  if (cItem?.nodeType === 1 && dom.check.isEmptyLine(cItem)) {
705
769
  dom.utils.removeItem(cItem);
706
770
  }
707
771
  }
708
772
 
709
- if (freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) {
710
- oNode = this._setIntoFreeFormat(oNode);
773
+ if (freeFormat && !dom.check.isList(oNode) && (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode))) {
774
+ oNode = this.#setIntoFreeFormat(oNode);
711
775
  }
712
776
 
713
- if (!this.component.isBasic(oNode)) {
777
+ if (!this.#$.component.isBasic(oNode)) {
714
778
  let offset = 1;
715
779
  if (oNode.nodeType === 3) {
716
780
  offset = oNode.textContent.length;
717
- this.selection.setRange(oNode, offset, oNode, offset);
718
- } else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.format.isLine(parentNode)) {
781
+ this.#$.selection.setRange(oNode, offset, oNode, offset);
782
+ } else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.#$.format.isLine(parentNode)) {
719
783
  let zeroWidth = null;
720
784
  if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
721
785
  zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
@@ -727,12 +791,12 @@ HTML.prototype = {
727
791
  oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
728
792
  }
729
793
 
730
- if (this.format._isIgnoreNodeChange(oNode)) {
794
+ if (this.#$.inline._isIgnoreNodeChange(oNode)) {
731
795
  oNode = oNode.nextSibling;
732
796
  offset = 0;
733
797
  }
734
798
 
735
- this.selection.setRange(oNode, offset, oNode, offset);
799
+ this.#$.selection.setRange(oNode, offset, oNode, offset);
736
800
  }
737
801
  }
738
802
 
@@ -740,33 +804,32 @@ HTML.prototype = {
740
804
  }
741
805
 
742
806
  return result;
743
- },
807
+ }
744
808
 
745
809
  /**
746
- * @this {HTMLThis}
747
810
  * @description Delete the selected range.
748
- * @returns {{container: Node, offset: number, commonCon?: Node|null, prevContainer?: Node|null}}
749
- * - container: "the last element after deletion"
750
- * - offset: "offset"
751
- * - commonCon: "commonAncestorContainer"
752
- * - prevContainer: "previousElementSibling Of the deleted area"
811
+ * @returns {{container: Node, offset: number, commonCon?: ?Node, prevContainer?: ?Node}}
812
+ * - `container`: the last element after deletion
813
+ * - `offset`: offset
814
+ * - `commonCon`: `commonAncestorContainer`
815
+ * - `prevContainer`: `previousElementSibling` of the deleted area
753
816
  */
754
817
  remove() {
755
- this.selection._resetRangeToTextNode();
818
+ this.#$.selection.resetRangeToTextNode();
756
819
 
757
- const range = this.selection.getRange();
820
+ const range = this.#$.selection.getRange();
758
821
  const isStartEdge = range.startOffset === 0;
759
822
  const isEndEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
760
823
  let prevContainer = null;
761
824
  let startPrevEl = null;
762
825
  let endNextEl = null;
763
826
  if (isStartEdge) {
764
- startPrevEl = this.format.getLine(range.startContainer);
827
+ startPrevEl = this.#$.format.getLine(range.startContainer);
765
828
  prevContainer = startPrevEl ? startPrevEl.previousElementSibling : null;
766
829
  startPrevEl = startPrevEl ? prevContainer : startPrevEl;
767
830
  }
768
831
  if (isEndEdge) {
769
- endNextEl = this.format.getLine(range.endContainer);
832
+ endNextEl = this.#$.format.getLine(range.endContainer);
770
833
  endNextEl = endNextEl ? endNextEl.nextElementSibling : endNextEl;
771
834
  }
772
835
 
@@ -777,9 +840,20 @@ HTML.prototype = {
777
840
  let startOff = range.startOffset;
778
841
  let endOff = range.endOffset;
779
842
  const commonCon = /** @type {HTMLElement} */ (range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer);
843
+
844
+ if (dom.check.isWysiwygFrame(startCon) && dom.check.isWysiwygFrame(endCon)) {
845
+ this.set('');
846
+ const newInitBR = this.#$.selection.getNode();
847
+ return {
848
+ container: newInitBR,
849
+ offset: 0,
850
+ commonCon,
851
+ };
852
+ }
853
+
780
854
  if (commonCon === startCon && commonCon === endCon) {
781
- if (this.component.isBasic(commonCon)) {
782
- const compInfo = this.component.get(commonCon);
855
+ if (this.#$.component.isBasic(commonCon)) {
856
+ const compInfo = this.#$.component.get(commonCon);
783
857
  const compContainer = compInfo.container;
784
858
  const parent = compContainer.parentElement;
785
859
 
@@ -790,39 +864,39 @@ HTML.prototype = {
790
864
 
791
865
  dom.utils.removeItem(compContainer);
792
866
 
793
- if (this.format.isLine(parent)) {
867
+ if (this.#$.format.isLine(parent)) {
794
868
  if (parent.childNodes.length === 0) {
795
869
  dom.utils.removeItem(parent);
796
870
  return {
797
871
  container: parentNext,
798
872
  offset: parentNextOffset,
799
- commonCon
873
+ commonCon,
800
874
  };
801
875
  } else {
802
876
  return {
803
877
  container: next,
804
878
  offset: nextOffset,
805
- commonCon
879
+ commonCon,
806
880
  };
807
881
  }
808
882
  } else {
809
883
  return {
810
884
  container: parentNext,
811
885
  offset: parentNextOffset,
812
- commonCon
886
+ commonCon,
813
887
  };
814
888
  }
815
889
  } else {
816
890
  if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
817
- const nextEl = dom.query.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
818
- const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
819
- const line = this.format.getLine(commonCon);
891
+ const nextEl = dom.query.getNextDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
892
+ const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
893
+ const line = this.#$.format.getLine(commonCon);
820
894
  dom.utils.removeItem(commonCon);
821
895
 
822
896
  let rEl = nextEl || prevEl;
823
897
  let rOffset = nextEl ? 0 : rEl?.nodeType === 3 ? rEl.textContent.length : 1;
824
898
 
825
- const npEl = this.format.getLine(rEl) || this.component.get(rEl);
899
+ const npEl = this.#$.format.getLine(rEl) || this.#$.component.get(rEl);
826
900
  if (line !== npEl) {
827
901
  rEl = /** @type {Node} */ (npEl);
828
902
  rOffset = rOffset === 0 ? 0 : 1;
@@ -835,7 +909,7 @@ HTML.prototype = {
835
909
  return {
836
910
  container: rEl,
837
911
  offset: rOffset,
838
- commonCon
912
+ commonCon,
839
913
  };
840
914
  }
841
915
 
@@ -849,7 +923,7 @@ HTML.prototype = {
849
923
  return {
850
924
  container: commonCon,
851
925
  offset: 0,
852
- commonCon
926
+ commonCon,
853
927
  };
854
928
 
855
929
  if (startCon === endCon && range.collapsed) {
@@ -858,7 +932,7 @@ HTML.prototype = {
858
932
  container: startCon,
859
933
  offset: startOff,
860
934
  prevContainer: startCon && startCon.parentNode ? startCon : null,
861
- commonCon
935
+ commonCon,
862
936
  };
863
937
  }
864
938
  }
@@ -866,7 +940,7 @@ HTML.prototype = {
866
940
  let beforeNode = null;
867
941
  let afterNode = null;
868
942
 
869
- const childNodes = dom.query.getListChildNodes(commonCon, null);
943
+ const childNodes = dom.query.getListChildNodes(commonCon, null, null);
870
944
  let startIndex = dom.utils.getArrayIndex(childNodes, startCon);
871
945
  let endIndex = dom.utils.getArrayIndex(childNodes, endCon);
872
946
 
@@ -887,17 +961,17 @@ HTML.prototype = {
887
961
  }
888
962
  } else {
889
963
  if (childNodes.length === 0) {
890
- if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
964
+ if (this.#$.format.isLine(commonCon) || this.#$.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
891
965
  return {
892
966
  container: commonCon,
893
967
  offset: 0,
894
- commonCon
968
+ commonCon,
895
969
  };
896
970
  } else if (dom.check.isText(commonCon)) {
897
971
  return {
898
972
  container: commonCon,
899
973
  offset: endOff,
900
- commonCon
974
+ commonCon,
901
975
  };
902
976
  }
903
977
  childNodes.push(commonCon);
@@ -908,7 +982,7 @@ HTML.prototype = {
908
982
  return {
909
983
  container: dom.check.isMedia(commonCon) ? commonCon : startCon,
910
984
  offset: 0,
911
- commonCon
985
+ commonCon,
912
986
  };
913
987
  }
914
988
  }
@@ -918,17 +992,19 @@ HTML.prototype = {
918
992
 
919
993
  const _isText = dom.check.isText;
920
994
  const _isElement = dom.check.isElement;
995
+ const _isSingleItem = startIndex === endIndex;
996
+ let nextFocusNodes = null;
921
997
  for (let i = startIndex; i <= endIndex; i++) {
922
998
  const item = /** @type {Text} */ (childNodes[i]);
923
999
 
924
1000
  if (_isText(item) && (item.data === undefined || item.length === 0)) {
925
- this._nodeRemoveListItem(item);
1001
+ nextFocusNodes = this.#nodeRemoveListItem(item, _isSingleItem);
926
1002
  continue;
927
1003
  }
928
1004
 
929
1005
  if (item === startCon) {
930
1006
  if (_isElement(startCon)) {
931
- if (this.component.is(startCon)) continue;
1007
+ if (this.#$.component.is(startCon)) continue;
932
1008
  else beforeNode = dom.utils.createTextNode(startCon.textContent);
933
1009
  } else {
934
1010
  const sc = /** @type {Text} */ (startCon);
@@ -944,7 +1020,7 @@ HTML.prototype = {
944
1020
  if (beforeNode.length > 0) {
945
1021
  /** @type {Text} */ (startCon).data = beforeNode.data;
946
1022
  } else {
947
- this._nodeRemoveListItem(startCon);
1023
+ nextFocusNodes = this.#nodeRemoveListItem(startCon, _isSingleItem);
948
1024
  }
949
1025
 
950
1026
  if (item === endCon) break;
@@ -955,20 +1031,20 @@ HTML.prototype = {
955
1031
  if (_isText(endCon)) {
956
1032
  afterNode = dom.utils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
957
1033
  } else {
958
- if (this.component.is(endCon)) continue;
1034
+ if (this.#$.component.is(endCon)) continue;
959
1035
  else afterNode = dom.utils.createTextNode(endCon.textContent);
960
1036
  }
961
1037
 
962
1038
  if (afterNode.length > 0) {
963
1039
  /** @type {Text} */ (endCon).data = afterNode.data;
964
1040
  } else {
965
- this._nodeRemoveListItem(endCon);
1041
+ nextFocusNodes = this.#nodeRemoveListItem(endCon, _isSingleItem);
966
1042
  }
967
1043
 
968
1044
  continue;
969
1045
  }
970
1046
 
971
- this._nodeRemoveListItem(item);
1047
+ nextFocusNodes = this.#nodeRemoveListItem(item, _isSingleItem);
972
1048
  }
973
1049
 
974
1050
  const endUl = dom.query.getParentElement(endCon, 'ul');
@@ -992,7 +1068,7 @@ HTML.prototype = {
992
1068
  }
993
1069
  }
994
1070
 
995
- if (!this.format.getLine(container) && !(startCon && startCon.parentNode)) {
1071
+ if (!this.#$.format.getLine(container) && !(startCon && startCon.parentNode)) {
996
1072
  if (endNextEl) {
997
1073
  container = endNextEl;
998
1074
  offset = 0;
@@ -1003,58 +1079,65 @@ HTML.prototype = {
1003
1079
  }
1004
1080
 
1005
1081
  if (!dom.check.isWysiwygFrame(container) && container.childNodes.length === 0) {
1006
- const rc = this.nodeTransform.removeAllParents(container, null, null);
1007
- if (rc) container = rc.sc || rc.ec || this.editor.frameContext.get('wysiwyg');
1082
+ const rc = this.#$.nodeTransform.removeAllParents(container, null, null);
1083
+ if (rc) container = rc.sc || rc.ec || this.#frameContext.get('wysiwyg');
1084
+ }
1085
+
1086
+ if (!container || (container.nodeType === 1 && !this.#$.format.isLine(container) && !dom.check.isBreak(container))) {
1087
+ container = nextFocusNodes?.sc || nextFocusNodes?.ec;
1088
+ offset = container?.nodeType === 3 ? container.textContent.length : 1;
1008
1089
  }
1009
1090
 
1010
1091
  // set range
1011
- this.selection.setRange(container, offset, container, offset);
1092
+ this.#$.selection.setRange(container, offset, container, offset);
1012
1093
 
1013
1094
  return {
1014
1095
  container,
1015
1096
  offset,
1016
1097
  prevContainer,
1017
- commonCon
1098
+ commonCon: commonCon?.parentElement ? commonCon : null,
1018
1099
  };
1019
- },
1100
+ }
1020
1101
 
1021
1102
  /**
1022
- * @this {HTMLThis}
1023
1103
  * @description Gets the current content
1024
1104
  * @param {Object} [options] Options
1025
- * @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
1026
- * Ignored for targetOptions.get('iframe_fullPage') is true.
1027
- * @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the "iframe_fullPage" option is true
1105
+ * @param {boolean} [options.withFrame=false] Gets the current content with containing parent `div.sun-editor-editable` (`<div class="sun-editor-editable">{content}</div>`).
1106
+ * Ignored for `targetOptions.get('iframe_fullPage')` is `true`.
1107
+ * @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the `iframe_fullPage` option is `true`
1028
1108
  * @param {number|Array<number>} [options.rootKey=null] Root index
1029
1109
  * @returns {string|Object<*, string>}
1030
1110
  */
1031
1111
  get({ withFrame, includeFullPage, rootKey } = {}) {
1032
- if (!rootKey) rootKey = [this.status.rootKey];
1112
+ if (!rootKey) rootKey = [this.#store.get('rootKey')];
1033
1113
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1034
1114
 
1035
- const prevrootKey = this.status.rootKey;
1115
+ const prevrootKey = this.#store.get('rootKey');
1036
1116
  const resultValue = {};
1037
1117
  for (let i = 0, len = rootKey.length, r; i < len; i++) {
1038
- this.editor.changeFrameContext(rootKey[i]);
1118
+ this.#$.facade.changeFrameContext(rootKey[i]);
1039
1119
 
1040
- const fc = this.editor.frameContext;
1041
- const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
1120
+ const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(this.#frameContext.get('wysiwyg'), true));
1042
1121
 
1043
1122
  const isTableCell = dom.check.isTableCell;
1044
1123
  const isEmptyLine = dom.check.isEmptyLine;
1045
1124
  const editableEls = [];
1046
1125
  const emptyCells = [];
1047
- dom.query.getListChildren(renderHTML, (current) => {
1048
- if (current.hasAttribute('contenteditable')) {
1049
- editableEls.push(current);
1050
- }
1126
+ dom.query.getListChildren(
1127
+ renderHTML,
1128
+ (current) => {
1129
+ if (current.hasAttribute('contenteditable')) {
1130
+ editableEls.push(current);
1131
+ }
1051
1132
 
1052
- const parent = current.parentElement;
1053
- if (isTableCell(parent) && parent.children.length <= 1 && isEmptyLine(current)) {
1054
- emptyCells.push(parent);
1055
- }
1056
- return false;
1057
- });
1133
+ const parent = current.parentElement;
1134
+ if (isTableCell(parent) && parent.children.length <= 1 && isEmptyLine(current)) {
1135
+ emptyCells.push(parent);
1136
+ }
1137
+ return false;
1138
+ },
1139
+ null,
1140
+ );
1058
1141
 
1059
1142
  for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
1060
1143
  editableEls[j].removeAttribute('contenteditable');
@@ -1064,97 +1147,94 @@ HTML.prototype = {
1064
1147
  }
1065
1148
 
1066
1149
  const content = renderHTML.innerHTML;
1067
- if (this.editor.frameOptions.get('iframe_fullPage')) {
1150
+ if (this.#frameOptions.get('iframe_fullPage')) {
1068
1151
  if (includeFullPage) {
1069
- const attrs = dom.utils.getAttributesToString(fc.get('_wd').body, ['contenteditable']);
1070
- r = `<!DOCTYPE html><html>${fc.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
1152
+ const attrs = dom.utils.getAttributesToString(this.#frameContext.get('_wd').body, ['contenteditable']);
1153
+ r = `<!DOCTYPE html><html>${this.#frameContext.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
1071
1154
  } else {
1072
1155
  r = content;
1073
1156
  }
1074
1157
  } else {
1075
- r = withFrame ? `<div class="${this.options.get('_editableClass') + '' + (this.options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
1158
+ r = withFrame ? `<div class="${this.#options.get('_editableClass') + '' + (this.#options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
1076
1159
  }
1077
1160
 
1078
1161
  resultValue[rootKey[i]] = r;
1079
1162
  }
1080
1163
 
1081
- this.editor.changeFrameContext(prevrootKey);
1164
+ this.#$.facade.changeFrameContext(prevrootKey);
1082
1165
  return rootKey.length > 1 ? resultValue : resultValue[rootKey[0]];
1083
- },
1166
+ }
1084
1167
 
1085
1168
  /**
1086
- * @this {HTMLThis}
1087
1169
  * @description Sets the HTML string to the editor content
1088
1170
  * @param {string} html HTML string
1089
1171
  * @param {Object} [options] Options
1090
1172
  * @param {number|Array<number>} [options.rootKey=null] Root index
1091
1173
  */
1092
1174
  set(html, { rootKey } = {}) {
1093
- this.selection.removeRange();
1175
+ this.#$.ui.offCurrentController();
1176
+ this.#$.selection.removeRange();
1094
1177
  const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
1095
1178
 
1096
- if (!rootKey) rootKey = [this.status.rootKey];
1179
+ if (!rootKey) rootKey = [this.#store.get('rootKey')];
1097
1180
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1098
1181
 
1099
1182
  for (let i = 0; i < rootKey.length; i++) {
1100
- this.editor.changeFrameContext(rootKey[i]);
1183
+ this.#$.facade.changeFrameContext(rootKey[i]);
1101
1184
 
1102
- if (!this.editor.frameContext.get('isCodeView')) {
1103
- this.editor.frameContext.get('wysiwyg').innerHTML = convertValue;
1104
- this.editor._resetComponents();
1105
- this.history.push(false, rootKey[i]);
1185
+ if (!this.#frameContext.get('isCodeView')) {
1186
+ this.#frameContext.get('wysiwyg').innerHTML = convertValue;
1187
+ this.#$.pluginManager.resetFileInfo();
1188
+ this.#$.history.push(false, rootKey[i]);
1106
1189
  } else {
1107
1190
  const value = this._convertToCode(convertValue, false);
1108
- this.viewer._setCodeView(value);
1191
+ this.#$.viewer._setCodeView(value);
1109
1192
  }
1110
1193
  }
1111
- },
1194
+ }
1112
1195
 
1113
1196
  /**
1114
- * @this {HTMLThis}
1115
1197
  * @description Add content to the end of content.
1116
1198
  * @param {string} html Content to Input
1117
1199
  * @param {Object} [options] Options
1118
1200
  * @param {number|Array<number>} [options.rootKey=null] Root index
1119
1201
  */
1120
1202
  add(html, { rootKey } = {}) {
1121
- if (!rootKey) rootKey = [this.status.rootKey];
1203
+ this.#$.ui.offCurrentController();
1204
+
1205
+ if (!rootKey) rootKey = [this.#store.get('rootKey')];
1122
1206
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1123
1207
 
1124
1208
  for (let i = 0; i < rootKey.length; i++) {
1125
- this.editor.changeFrameContext(rootKey[i]);
1209
+ this.#$.facade.changeFrameContext(rootKey[i]);
1126
1210
  const convertValue = this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
1127
1211
 
1128
- if (!this.editor.frameContext.get('isCodeView')) {
1212
+ if (!this.#frameContext.get('isCodeView')) {
1129
1213
  const temp = dom.utils.createElement('DIV', null, convertValue);
1130
- const children = temp.children;
1131
- const len = children.length;
1132
- for (let j = 0; j < len; j++) {
1133
- if (!children[j]) continue;
1134
- this.editor.frameContext.get('wysiwyg').appendChild(children[j]);
1214
+ const children = Array.from(temp.children);
1215
+ for (let j = 0, jLen = children.length; j < jLen; j++) {
1216
+ this.#frameContext.get('wysiwyg').appendChild(children[j]);
1135
1217
  }
1136
- this.history.push(false, rootKey[i]);
1137
- this.selection.scrollTo(children[len - 1]);
1218
+ this.#$.history.push(false, rootKey[i]);
1219
+ this.#$.selection.scrollTo(children.at(-1));
1138
1220
  } else {
1139
- this.viewer._setCodeView(this.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false));
1221
+ this.#$.viewer._setCodeView(this.#$.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false));
1140
1222
  }
1141
1223
  }
1142
- },
1224
+ }
1143
1225
 
1144
1226
  /**
1145
- * @this {HTMLThis}
1146
1227
  * @description Gets the current content to JSON data
1147
1228
  * @param {Object} [options] Options
1148
- * @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
1229
+ * @param {boolean} [options.withFrame=false] Gets the current content with containing parent `div.sun-editor-editable` (`<div class="sun-editor-editable">{content}</div>`).
1149
1230
  * @param {number|Array<number>} [options.rootKey=null] Root index
1150
1231
  * @returns {Object<string, *>} JSON data
1151
1232
  */
1152
1233
  getJson({ withFrame, rootKey } = {}) {
1153
1234
  return converter.htmlToJson(this.get({ withFrame, rootKey }));
1154
- },
1235
+ }
1155
1236
 
1156
1237
  /**
1157
- * @this {HTMLThis}
1158
1238
  * @description Sets the JSON data to the editor content
1159
1239
  * @param {Object<string, *>} jsdonData HTML string
1160
1240
  * @param {Object} [options] Options
@@ -1162,80 +1242,83 @@ HTML.prototype = {
1162
1242
  */
1163
1243
  setJson(jsdonData, { rootKey } = {}) {
1164
1244
  this.set(converter.jsonToHtml(jsdonData), { rootKey });
1165
- },
1245
+ }
1166
1246
 
1167
1247
  /**
1168
- * @this {HTMLThis}
1169
- * @description Call "clipboard.write" to copy the contents and display a success/failure toast message.
1170
- * @param {Element|Text|string} content Content to be copied to the clipboard
1248
+ * @description Call `clipboard.write` to copy the contents and display a success/failure toast message.
1249
+ * @param {Node|Element|Text|string} content Content to be copied to the clipboard
1171
1250
  * @returns {Promise<boolean>} Success or failure
1172
1251
  */
1173
1252
  async copy(content) {
1174
1253
  try {
1175
- await clipboard.write(content);
1176
- this.editor.ui.showToast(this.lang.message_copy_success, this.options.get('toastMessageTime').copy);
1254
+ if (typeof content !== 'string' && !dom.check.isElement(content) && !dom.check.isText(content)) return false;
1255
+
1256
+ if ((await clipboard.write(content)) === false) {
1257
+ this.#$.ui.showToast(this.#$.lang.message_copy_fail, this.#options.get('toastMessageTime').copy, 'error');
1258
+ return false;
1259
+ }
1260
+ this.#$.ui.showToast(this.#$.lang.message_copy_success, this.#options.get('toastMessageTime').copy);
1177
1261
  return true;
1178
1262
  } catch (err) {
1179
1263
  console.error('[SUNEDITOR.html.copy.fail] :', err);
1180
- this.editor.ui.showToast(this.lang.message_copy_fail, this.options.get('toastMessageTime').copy, 'error');
1264
+ this.#$.ui.showToast(this.#$.lang.message_copy_fail, this.#options.get('toastMessageTime').copy, 'error');
1181
1265
  return false;
1182
1266
  }
1183
- },
1267
+ }
1184
1268
 
1185
1269
  /**
1186
- * @this {HTMLThis}
1187
- * @description Sets the content of the iframe's head tag and body tag when using the "iframe" or "iframe_fullPage" option.
1270
+ * @description Sets the content of the iframe's head tag and body tag when using the `iframe` or `iframe_fullPage` option.
1188
1271
  * @param {{head: string, body: string}} ctx { head: HTML string, body: HTML string}
1189
1272
  * @param {Object} [options] Options
1190
1273
  * @param {number|Array<number>} [options.rootKey=null] Root index
1191
1274
  */
1192
1275
  setFullPage(ctx, { rootKey } = {}) {
1193
- if (!this.editor.frameOptions.get('iframe')) return false;
1276
+ if (!this.#frameOptions.get('iframe')) return false;
1194
1277
 
1195
- if (!rootKey) rootKey = [this.status.rootKey];
1278
+ if (!rootKey) rootKey = [this.#store.get('rootKey')];
1196
1279
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1197
1280
 
1198
1281
  for (let i = 0; i < rootKey.length; i++) {
1199
- this.editor.changeFrameContext(rootKey[i]);
1200
- if (ctx.head) this.editor.frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.__disallowedTagsRegExp, '');
1201
- if (ctx.body) this.editor.frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
1202
- this.editor._resetComponents();
1282
+ this.#$.facade.changeFrameContext(rootKey[i]);
1283
+ if (ctx.head) this.#frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.#disallowedTagsRegExp, '');
1284
+ if (ctx.body) this.#frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
1285
+ this.#$.pluginManager.resetFileInfo();
1203
1286
  }
1204
- },
1287
+ }
1205
1288
 
1206
1289
  /**
1207
- * @this {HTMLThis}
1208
1290
  * @description HTML code compression
1209
1291
  * @param {string} html HTML string
1210
1292
  * @returns {string} HTML string
1211
1293
  */
1212
1294
  compress(html) {
1213
1295
  return html.replace(/>\s+</g, '> <').replace(/\n/g, '').trim();
1214
- },
1296
+ }
1215
1297
 
1216
1298
  /**
1217
- * @private
1218
- * @this {HTMLThis}
1299
+ * @internal
1219
1300
  * @description construct wysiwyg area element to html string
1220
- * @param {Node|string} html WYSIWYG element (this.editor.frameContext.get('wysiwyg')) or HTML string.
1221
- * @param {boolean} comp If true, does not line break and indentation of tags.
1301
+ * @param {Node|string} html WYSIWYG element (this.#frameContext.get('wysiwyg')) or HTML string.
1302
+ * @param {boolean} comp If `true`, does not line break and indentation of tags.
1222
1303
  * @returns {string}
1223
1304
  */
1224
1305
  _convertToCode(html, comp) {
1225
1306
  let returnHTML = '';
1226
1307
  const wRegExp = RegExp;
1227
1308
  const brReg = new wRegExp('^(BLOCKQUOTE|PRE|TABLE|THEAD|TBODY|TR|TH|TD|OL|UL|IMG|IFRAME|VIDEO|AUDIO|FIGURE|FIGCAPTION|HR|BR|CANVAS|SELECT)$', 'i');
1228
- const wDoc = typeof html === 'string' ? this._d.createRange().createContextualFragment(html) : html;
1309
+ const wDoc = typeof html === 'string' ? _d.createRange().createContextualFragment(html) : html;
1229
1310
  const isFormat = (current) => {
1230
- return this.format.isLine(current) || this.component.is(current);
1311
+ return this.#$.format.isLine(current) || this.#$.component.is(current);
1231
1312
  };
1232
1313
  const brChar = comp ? '' : '\n';
1233
1314
 
1234
- const codeSize = comp ? 0 : this.status.codeIndentSize * 1;
1315
+ const codeSize = comp ? 0 : this.#store.get('codeIndentSize') * 1;
1235
1316
  const indentSize = codeSize > 0 ? new Array(codeSize + 1).join(' ') : '';
1236
1317
 
1237
1318
  (function recursionFunc(element, indent) {
1238
- const children = element.childNodes;
1319
+ const children = element?.childNodes;
1320
+ if (!children) return;
1321
+
1239
1322
  const elementRegTest = brReg.test(element.nodeName);
1240
1323
  const elementIndent = elementRegTest ? indent : '';
1241
1324
 
@@ -1271,46 +1354,46 @@ HTML.prototype = {
1271
1354
  })(wDoc, '');
1272
1355
 
1273
1356
  return returnHTML.trim() + brChar;
1274
- },
1357
+ }
1275
1358
 
1276
1359
  /**
1277
- * @private
1278
- * @this {HTMLThis}
1279
1360
  * @description Checks whether the given list item node should be removed and handles necessary clean-up.
1280
1361
  * @param {Node} item The list item node to be checked.
1362
+ * @param {boolean} isSingleItem Single item
1363
+ * @returns {{sc:Node, ec:Node}|null} An object containing the start and end containers if any transformations were made, otherwise `null`.
1281
1364
  */
1282
- _nodeRemoveListItem(item) {
1283
- const line = this.format.getLine(item, null);
1365
+ #nodeRemoveListItem(item, isSingleItem) {
1366
+ const line = this.#$.format.getLine(item, null);
1284
1367
  dom.utils.removeItem(item);
1285
1368
 
1286
- if (!dom.check.isListCell(line)) return;
1369
+ if (!dom.check.isListCell(line) || isSingleItem) return;
1287
1370
 
1288
- this.nodeTransform.removeAllParents(line, null, null);
1371
+ const result = this.#$.nodeTransform.removeAllParents(line, null, null);
1289
1372
 
1290
1373
  if (dom.check.isList(line?.firstChild)) {
1291
1374
  line.insertBefore(dom.utils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
1292
1375
  }
1293
- },
1376
+
1377
+ return result ? { sc: result.sc, ec: result.ec } : null;
1378
+ }
1294
1379
 
1295
1380
  /**
1296
- * @private
1297
- * @this {HTMLThis}
1298
- * @description Recursive function when used to place a node in "BrLine" in "html.insertNode"
1381
+ * @description Recursive function when used to place a node in `BrLine` in `html.insertNode`
1299
1382
  * @param {Node} oNode Node to be inserted
1300
- * @returns {Node} "oNode"
1383
+ * @returns {Node} `oNode`
1301
1384
  */
1302
- _setIntoFreeFormat(oNode) {
1385
+ #setIntoFreeFormat(oNode) {
1303
1386
  const parentNode = oNode.parentNode;
1304
1387
  let oNodeChildren, lastONode;
1305
1388
 
1306
- while (this.format.isLine(oNode) || this.format.isBlock(oNode)) {
1389
+ while (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode)) {
1307
1390
  oNodeChildren = oNode.childNodes;
1308
1391
  lastONode = null;
1309
1392
 
1310
1393
  while (oNodeChildren[0]) {
1311
1394
  lastONode = oNodeChildren[0];
1312
- if (this.format.isLine(lastONode) || this.format.isBlock(lastONode)) {
1313
- this._setIntoFreeFormat(lastONode);
1395
+ if (this.#$.format.isLine(lastONode) || this.#$.format.isBlock(lastONode)) {
1396
+ this.#setIntoFreeFormat(lastONode);
1314
1397
  if (!oNode.parentNode) break;
1315
1398
  oNodeChildren = oNode.childNodes;
1316
1399
  continue;
@@ -1325,26 +1408,28 @@ HTML.prototype = {
1325
1408
  }
1326
1409
 
1327
1410
  return oNode;
1328
- },
1411
+ }
1329
1412
 
1330
1413
  /**
1331
- * @private
1332
- * @this {HTMLThis}
1333
- * @description Returns HTML string according to tag type and configurati isExcludeFormat.
1414
+ * @description Returns HTML string according to tag type and configuration `isExcludeFormat`.
1334
1415
  * @param {Node} node Node
1335
- * @param {boolean} forceFormat If true, text nodes that do not have a format node is wrapped with the format tag.
1416
+ * @param {boolean} forceFormat If `true`, text nodes that do not have a format node is wrapped with the format tag.
1336
1417
  */
1337
- _makeLine(node, forceFormat) {
1338
- const defaultLine = this.options.get('defaultLine');
1418
+ #makeLine(node, forceFormat) {
1419
+ const defaultLine = this.#options.get('defaultLine');
1339
1420
  // element
1340
1421
  if (node.nodeType === 1) {
1341
- if (this.__disallowedTagNameRegExp.test(node.nodeName)) return '';
1422
+ if (this.#disallowedTagNameRegExp.test(node.nodeName)) return '';
1342
1423
  if (dom.check.isExcludeFormat(node)) return node.outerHTML;
1343
1424
 
1344
1425
  const ch =
1345
- dom.query.getListChildNodes(node, (current) => {
1346
- return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1347
- }) || [];
1426
+ dom.query.getListChildNodes(
1427
+ node,
1428
+ (current) => {
1429
+ return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1430
+ },
1431
+ null,
1432
+ ) || [];
1348
1433
  for (let i = ch.length - 1, c; i >= 0; i--) {
1349
1434
  c = /** @type {HTMLElement} */ (ch[i]);
1350
1435
  c.outerHTML = c.innerHTML;
@@ -1352,9 +1437,9 @@ HTML.prototype = {
1352
1437
 
1353
1438
  if (
1354
1439
  !forceFormat ||
1355
- this.format.isLine(node) ||
1356
- this.format.isBlock(node) ||
1357
- this.component.is(node) ||
1440
+ this.#$.format.isLine(node) ||
1441
+ this.#$.format.isBlock(node) ||
1442
+ this.#$.component.is(node) ||
1358
1443
  dom.check.isMedia(node) ||
1359
1444
  dom.check.isFigure(node) ||
1360
1445
  (dom.check.isAnchor(node) && dom.check.isMedia(node.firstElementChild))
@@ -1378,18 +1463,16 @@ HTML.prototype = {
1378
1463
  return html;
1379
1464
  }
1380
1465
  // comments
1381
- if (node.nodeType === 8 && this._allowHTMLComment) {
1466
+ if (node.nodeType === 8 && this.#allowHTMLComment) {
1382
1467
  return '<!--' + node.textContent.trim() + '-->';
1383
1468
  }
1384
1469
 
1385
1470
  return '';
1386
- },
1471
+ }
1387
1472
 
1388
1473
  /**
1389
- * @private
1390
- * @this {HTMLThis}
1391
1474
  * @description Fix tags that do not fit the editor format.
1392
- * @param {DocumentFragment} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
1475
+ * @param {DocumentFragment} documentFragment Document fragment `DOCUMENT_FRAGMENT_NODE` (nodeType === 11)
1393
1476
  * @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist
1394
1477
  * @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist
1395
1478
  * @param {boolean} tagFilter Tag filter option
@@ -1397,84 +1480,89 @@ HTML.prototype = {
1397
1480
  * @param {boolean} classFilter Class name filter option
1398
1481
  * @param {boolean} _freeCodeViewMode Enforces strict HTML validation based on the editor`s policy
1399
1482
  */
1400
- _consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
1483
+ #consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
1401
1484
  const removeTags = [],
1402
1485
  emptyTags = [],
1403
1486
  wrongList = [],
1404
1487
  withoutFormatCells = [];
1405
1488
 
1406
1489
  // wrong position
1407
- const wrongTags = dom.query.getListChildNodes(documentFragment, (current) => {
1408
- if (formatFilter && current.nodeType !== 1) {
1409
- if (dom.check.isList(current.parentElement)) removeTags.push(current);
1410
- return false;
1411
- }
1412
-
1413
- // tag filter
1414
- if (tagFilter) {
1415
- // white list
1416
- if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
1417
- removeTags.push(current);
1490
+ const wrongTags = dom.query.getListChildNodes(
1491
+ documentFragment,
1492
+ (current) => {
1493
+ if (current.nodeType !== 1) {
1494
+ if (formatFilter && dom.check.isList(current.parentElement)) removeTags.push(current);
1495
+ if (current.nodeType === 3 && !current.textContent.trim()) removeTags.push(current);
1418
1496
  return false;
1419
1497
  }
1420
- }
1421
1498
 
1422
- const nrtag = !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1423
-
1424
- // formatFilter
1425
- if (formatFilter) {
1426
- // empty tags
1427
- if (
1428
- !dom.check.isTableElements(current) &&
1429
- !dom.check.isListCell(current) &&
1430
- !dom.check.isAnchor(current) &&
1431
- (this.format.isLine(current) || this.format.isBlock(current) || this.format.isTextStyleNode(current)) &&
1432
- current.childNodes.length === 0 &&
1433
- nrtag
1434
- ) {
1435
- emptyTags.push(current);
1436
- return false;
1499
+ // tag filter
1500
+ if (tagFilter) {
1501
+ // white list
1502
+ if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
1503
+ removeTags.push(current);
1504
+ return false;
1505
+ }
1437
1506
  }
1438
1507
 
1439
- // wrong list
1440
- if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
1441
- wrongList.push(current);
1442
- return false;
1443
- }
1508
+ const nrtag = !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1509
+
1510
+ // formatFilter
1511
+ if (formatFilter) {
1512
+ // empty tags
1513
+ if (
1514
+ !dom.check.isTableElements(current) &&
1515
+ !dom.check.isListCell(current) &&
1516
+ !dom.check.isAnchor(current) &&
1517
+ (this.#$.format.isLine(current) || this.#$.format.isBlock(current) || this.#$.format.isTextStyleNode(current)) &&
1518
+ current.childNodes.length === 0 &&
1519
+ nrtag
1520
+ ) {
1521
+ emptyTags.push(current);
1522
+ return false;
1523
+ }
1444
1524
 
1445
- // table cells
1446
- if (dom.check.isTableCell(current)) {
1447
- const fel = current.firstElementChild;
1448
- if (!this.format.isLine(fel) && !this.format.isBlock(fel) && !this.component.is(fel)) {
1449
- withoutFormatCells.push(current);
1525
+ // wrong list
1526
+ if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
1527
+ wrongList.push(current);
1450
1528
  return false;
1451
1529
  }
1530
+
1531
+ // table cells
1532
+ if (dom.check.isTableCell(current)) {
1533
+ const fel = current.firstElementChild;
1534
+ if (!this.#$.format.isLine(fel) && !this.#$.format.isBlock(fel) && !this.#$.component.is(fel)) {
1535
+ withoutFormatCells.push(current);
1536
+ return false;
1537
+ }
1538
+ }
1452
1539
  }
1453
- }
1454
1540
 
1455
- // class filter
1456
- if (classFilter) {
1457
- if (nrtag && current.className) {
1458
- const className = new Array(current.classList).map(this._isAllowedClassName).join(' ').trim();
1459
- if (className) current.className = className;
1460
- else current.removeAttribute('class');
1541
+ // class filter
1542
+ if (classFilter) {
1543
+ if (nrtag && current.className) {
1544
+ const className = Array.from(current.classList).map(this.#isAllowedClassName).join(' ').trim();
1545
+ if (className) current.className = className;
1546
+ else current.removeAttribute('class');
1547
+ }
1461
1548
  }
1462
- }
1463
1549
 
1464
- // format filter
1465
- if (!formatFilter) {
1466
- return false;
1467
- }
1550
+ // format filter
1551
+ if (!formatFilter) {
1552
+ return false;
1553
+ }
1468
1554
 
1469
- const result =
1470
- !_freeCodeViewMode &&
1471
- current.parentNode !== documentFragment &&
1472
- nrtag &&
1473
- ((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
1474
- ((this.format.isLine(current) || this.component.is(current)) && !this.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.component.is.bind(this.component))));
1555
+ const result =
1556
+ !_freeCodeViewMode &&
1557
+ current.parentNode !== documentFragment &&
1558
+ nrtag &&
1559
+ ((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
1560
+ ((this.#$.format.isLine(current) || this.#$.component.is(current)) && !this.#$.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.#$.component.is.bind(this.#$.component))));
1475
1561
 
1476
- return result;
1477
- });
1562
+ return result;
1563
+ },
1564
+ null,
1565
+ );
1478
1566
 
1479
1567
  for (let i = 0, len = removeTags.length; i < len; i++) {
1480
1568
  dom.utils.removeItem(removeTags[i]);
@@ -1488,8 +1576,8 @@ HTML.prototype = {
1488
1576
 
1489
1577
  if (dom.query.getParentElement(t, dom.check.isListCell)) {
1490
1578
  const cellChildren = t.childNodes;
1491
- for (let j = cellChildren.length - 1; len >= 0; j--) {
1492
- p.insertBefore(t, cellChildren[j]);
1579
+ for (let j = cellChildren.length - 1; j >= 0; j--) {
1580
+ p.insertBefore(cellChildren[j], t);
1493
1581
  }
1494
1582
  checkTags.push(t);
1495
1583
  } else {
@@ -1516,7 +1604,7 @@ HTML.prototype = {
1516
1604
 
1517
1605
  tp = dom.utils.createElement('LI');
1518
1606
 
1519
- if (this.format.isLine(t)) {
1607
+ if (this.#$.format.isLine(t)) {
1520
1608
  children = t.childNodes;
1521
1609
  while (children[0]) {
1522
1610
  tp.appendChild(children[0]);
@@ -1536,48 +1624,44 @@ HTML.prototype = {
1536
1624
  f.innerHTML = t.textContent.trim().length === 0 && t.children.length === 0 ? '<br>' : t.innerHTML;
1537
1625
  t.innerHTML = f.outerHTML;
1538
1626
  }
1539
- },
1627
+ }
1540
1628
 
1541
1629
  /**
1542
- * @private
1543
- * @this {HTMLThis}
1544
- * @description Removes attribute values such as style and converts tags that do not conform to the "html5" standard.
1630
+ * @description Removes attribute values such as style and converts tags that do not conform to the `html5` standard.
1545
1631
  * @param {string} html HTML string
1546
1632
  * @returns {string} HTML string
1547
1633
  */
1548
- _styleNodeConvertor(html) {
1549
- if (!this._disallowedStyleNodesRegExp) return html;
1634
+ #styleNodeConvertor(html) {
1635
+ if (!this.#disallowedStyleNodesRegExp) return html;
1550
1636
 
1551
- const ec = this.options.get('_defaultStyleTagMap');
1552
- return html.replace(this._disallowedStyleNodesRegExp, (m, t, n, p) => {
1637
+ const ec = this.#options.get('_defaultStyleTagMap');
1638
+ return html.replace(this.#disallowedStyleNodesRegExp, (m, t, n, p) => {
1553
1639
  return t + (typeof ec[n] === 'string' ? ec[n] : n) + (p ? ' ' + p : '');
1554
1640
  });
1555
- },
1641
+ }
1556
1642
 
1557
1643
  /**
1558
- * @private
1559
- * @this {HTMLThis}
1560
- * @description Determines if formatting is required and returns a domTree
1644
+ * @description Determines if formatting is required and returns a `domTree`
1561
1645
  * @param {DocumentFragment} domFrag documentFragment
1562
1646
  * @returns {DocumentFragment}
1563
1647
  */
1564
- _editFormat(domFrag) {
1648
+ #editFormat(domFrag) {
1565
1649
  let value = '',
1566
1650
  f;
1567
1651
  const tempTree = domFrag.childNodes;
1568
1652
 
1569
1653
  for (let i = 0, len = tempTree.length, n; i < len; i++) {
1570
1654
  n = /** @type {HTMLElement} */ (tempTree[i]);
1571
- if (this.__allowedTagNameRegExp.test(n.nodeName)) {
1655
+ if (this.#allowedTagNameRegExp.test(n.nodeName)) {
1572
1656
  value += n.outerHTML;
1573
1657
  continue;
1574
1658
  }
1575
1659
 
1576
1660
  if (n.nodeType === 8) {
1577
1661
  value += '<!-- ' + n.textContent + ' -->';
1578
- } else if (!/meta/i.test(n.nodeName) && !this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) && !dom.check.isExcludeFormat(n)) {
1579
- if (!f) f = dom.utils.createElement(this.options.get('defaultLine'));
1580
- if (this.format.isTextStyleNode(n)) {
1662
+ } else if (!/meta/i.test(n.nodeName) && !this.#$.format.isLine(n) && !this.#$.format.isBlock(n) && !this.#$.component.is(n) && !dom.check.isExcludeFormat(n)) {
1663
+ f ||= dom.utils.createElement(this.#options.get('defaultLine'));
1664
+ if (this.#$.format.isTextStyleNode(n)) {
1581
1665
  /** @type {HTMLElement} */
1582
1666
  (n).removeAttribute('style');
1583
1667
  }
@@ -1595,19 +1679,17 @@ HTML.prototype = {
1595
1679
 
1596
1680
  if (f) value += f.outerHTML;
1597
1681
 
1598
- return this._d.createRange().createContextualFragment(value);
1599
- },
1682
+ return _d.createRange().createContextualFragment(value);
1683
+ }
1600
1684
 
1601
1685
  /**
1602
- * @private
1603
- * @this {HTMLThis}
1604
1686
  * @description Converts a list of DOM nodes into an HTML list structure.
1605
- * - If the node is already a list, its innerHTML is used. If it is a block element,
1687
+ * - If the node is already a list, its `innerHTML` is used. If it is a block element,
1606
1688
  * - the function is called recursively.
1607
- * @param {__se__NodeCollection} domTree List of DOM nodes to be converted.
1689
+ * @param {SunEditor.NodeCollection} domTree List of DOM nodes to be converted.
1608
1690
  * @returns {string} The generated HTML list.
1609
1691
  */
1610
- _convertListCell(domTree) {
1692
+ #convertListCell(domTree) {
1611
1693
  let html = '';
1612
1694
 
1613
1695
  for (let i = 0, len = domTree.length, node; i < len; i++) {
@@ -1617,10 +1699,10 @@ HTML.prototype = {
1617
1699
  html += node.innerHTML;
1618
1700
  } else if (dom.check.isListCell(node)) {
1619
1701
  html += node.outerHTML;
1620
- } else if (this.format.isLine(node)) {
1702
+ } else if (this.#$.format.isLine(node)) {
1621
1703
  html += '<li>' + (node.innerHTML.trim() || '<br>') + '</li>';
1622
- } else if (this.format.isBlock(node) && !dom.check.isTableElements(node)) {
1623
- html += this._convertListCell(node.children);
1704
+ } else if (this.#$.format.isBlock(node) && !dom.check.isTableElements(node)) {
1705
+ html += this.#convertListCell(node.children);
1624
1706
  } else {
1625
1707
  html += '<li>' + /** @type {HTMLElement} */ (node).outerHTML + '</li>';
1626
1708
  }
@@ -1630,55 +1712,51 @@ HTML.prototype = {
1630
1712
  }
1631
1713
 
1632
1714
  return html;
1633
- },
1715
+ }
1634
1716
 
1635
1717
  /**
1636
- * @private
1637
- * @this {HTMLThis}
1638
1718
  * @description Checks whether the provided DOM nodes require formatting.
1639
1719
  * @param {NodeList} domTree List of DOM nodes to check.
1640
- * @returns {boolean} True if formatting is required, otherwise false.
1720
+ * @returns {boolean} `true` if formatting is required, otherwise `false`.
1641
1721
  */
1642
- _isFormatData(domTree) {
1722
+ #isFormatData(domTree) {
1643
1723
  let requireFormat = false;
1644
1724
 
1645
1725
  for (let i = 0, len = domTree.length, t; i < len; i++) {
1646
1726
  t = domTree[i];
1647
- if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.__disallowedTagNameRegExp.test(t.nodeName)) {
1727
+ if (t.nodeType === 1 && !this.#$.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.#disallowedTagNameRegExp.test(t.nodeName)) {
1648
1728
  requireFormat = true;
1649
1729
  break;
1650
1730
  }
1651
1731
  }
1652
1732
 
1653
1733
  return requireFormat;
1654
- },
1734
+ }
1655
1735
 
1656
1736
  /**
1657
- * @private
1658
- * @this {HTMLThis}
1659
1737
  * @description Cleans the inline style attributes of an HTML element.
1660
1738
  * - Extracts allowed styles and removes disallowed ones based on editor settings.
1661
1739
  * @param {string} m The full matched string from a regular expression.
1662
- * @param {Array|null} v The list of allowed attributes.
1740
+ * @param {?Array} v The list of allowed attributes.
1663
1741
  * @param {string} name The tag name of the element being cleaned.
1664
1742
  * @returns {Array} The updated list of allowed attributes including cleaned styles.
1665
1743
  */
1666
- _cleanStyle(m, v, name) {
1667
- let sv = (m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/) || [])[0];
1668
- if (this._textStyleTags.includes(name) && !sv && (m.match(/<[^\s]+\s(.+)/) || [])[1]) {
1669
- const size = (m.match(/\ssize="([^"]+)"/i) || [])[1];
1670
- const face = (m.match(/\sface="([^"]+)"/i) || [])[1];
1671
- const color = (m.match(/\scolor="([^"]+)"/i) || [])[1];
1744
+ #cleanStyle(m, v, name) {
1745
+ let sv = (m.match(_RE_STYLE_ATTR) || [])[0];
1746
+ if (this.#textStyleTags.includes(name) && !sv && (m.match(_RE_TAG_ATTRS) || [])[1]) {
1747
+ const size = (m.match(_RE_SIZE_ATTR) || [])[1];
1748
+ const face = (m.match(_RE_FACE_ATTR) || [])[1];
1749
+ const color = (m.match(_RE_COLOR_ATTR) || [])[1];
1672
1750
  if (size || face || color) {
1673
1751
  sv = 'style="' + (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
1674
1752
  }
1675
1753
  }
1676
1754
 
1677
1755
  if (sv) {
1678
- if (!v) v = [];
1756
+ v ||= [];
1679
1757
 
1680
1758
  let mv;
1681
- for (const [key, value] of this._cleanStyleRegExpMap) {
1759
+ for (const [key, value] of this.#cleanStyleRegExpMap) {
1682
1760
  if (key.test(name)) {
1683
1761
  mv = value;
1684
1762
  break;
@@ -1686,31 +1764,31 @@ HTML.prototype = {
1686
1764
  }
1687
1765
  if (!mv) return v;
1688
1766
 
1689
- const style = sv.replace(/&quot;/g, '').match(mv);
1767
+ const style = sv.replace(_RE_AMP_QUOT, '').match(mv);
1690
1768
  if (!style) return v;
1691
1769
 
1692
1770
  const allowedStyle = [];
1693
1771
  for (let i = 0, len = style.length, r; i < len; i++) {
1694
- r = style[i].match(/([a-zA-Z0-9-]+)(:)([^"']+)/);
1695
- if (r && !/inherit|initial|revert|unset/i.test(r[3])) {
1772
+ r = style[i].match(_RE_CSS_PROP);
1773
+ if (r && !_RE_CSS_GLOBAL.test(r[3])) {
1696
1774
  const k = converter.kebabToCamelCase(r[1].trim());
1697
- const cs = this.editor.frameContext.get('wwComputedStyle')[k]?.replace(/"/g, '');
1775
+ const cs = this.#frameContext.get('wwComputedStyle')[k]?.replace(_RE_DQUOTE, '');
1698
1776
  const c = r[3].trim();
1699
1777
  switch (k) {
1700
1778
  case 'fontFamily':
1701
- if (!this.plugins.font || !this.plugins.font.fontArray.includes(c)) continue;
1779
+ if (!this.#$.plugins.font || !this.#$.plugins.font.fontArray.includes(c)) continue;
1702
1780
  break;
1703
1781
  case 'fontSize':
1704
- if (!this.plugins.fontSize) continue;
1705
- if (!this.fontSizeUnitRegExp.test(r[0])) {
1706
- r[0] = r[0].replace((r[0].match(/:\s*([^;]+)/) || [])[1], converter.toFontUnit.bind(null, this.options.get('fontSizeUnits')[0]));
1782
+ if (!this.#$.plugins.fontSize) continue;
1783
+ if (!this.#fontSizeUnitRegExp.test(r[0])) {
1784
+ r[0] = r[0].replace((r[0].match(_RE_CSS_VALUE) || [])[1], converter.toFontUnit.bind(null, this.#options.get('fontSizeUnits')[0]));
1707
1785
  }
1708
1786
  break;
1709
1787
  case 'color':
1710
- if (!this.plugins.fontColor || /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i.test(c)) continue;
1788
+ if (!this.#$.plugins.fontColor || _RE_TRANSPARENT_COLOR.test(c)) continue;
1711
1789
  break;
1712
1790
  case 'backgroundColor':
1713
- if (!this.plugins.backgroundColor || /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i.test(c)) continue;
1791
+ if (!this.#$.plugins.backgroundColor || _RE_TRANSPARENT_COLOR.test(c)) continue;
1714
1792
  break;
1715
1793
  }
1716
1794
 
@@ -1723,67 +1801,60 @@ HTML.prototype = {
1723
1801
  }
1724
1802
 
1725
1803
  return v;
1726
- },
1804
+ }
1727
1805
 
1728
1806
  /**
1729
- * @private
1730
- * @this {HTMLThis}
1731
1807
  * @description Delete disallowed tags
1732
1808
  * @param {string} html HTML string
1733
1809
  * @returns {string}
1734
1810
  */
1735
- _deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
1811
+ #deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
1736
1812
  if (whitelistRegExp.test('<font>')) {
1737
1813
  html = html.replace(/(<\/?)font(\s?)/gi, '$1span$2');
1738
1814
  }
1739
1815
 
1740
1816
  return html.replace(whitelistRegExp, '').replace(blacklistRegExp, '');
1741
- },
1817
+ }
1742
1818
 
1743
1819
  /**
1744
- * @private
1745
- * @this {HTMLThis}
1746
1820
  * @description Recursively checks for duplicate text style nodes within a given parent node.
1747
1821
  * @param {Node} oNode The node to check for duplicate styles.
1748
1822
  * @param {Node} parentNode The parent node where the duplicate check occurs.
1749
1823
  */
1750
- _checkDuplicateNode(oNode, parentNode) {
1751
- // eslint-disable-next-line @typescript-eslint/no-this-alias
1752
- const inst = this;
1753
- (function recursionFunc(current) {
1754
- inst._dupleCheck(current, parentNode);
1824
+ #checkDuplicateNode(oNode, parentNode) {
1825
+ const recursionFunc = (current) => {
1826
+ this.#dupleCheck(current, parentNode);
1755
1827
  const childNodes = current.childNodes;
1756
1828
  for (let i = 0, len = childNodes.length; i < len; i++) {
1757
1829
  recursionFunc(childNodes[i]);
1758
1830
  }
1759
- })(oNode);
1760
- },
1831
+ };
1832
+ recursionFunc(oNode);
1833
+ }
1761
1834
 
1762
1835
  /**
1763
- * @private
1764
- * @this {HTMLThis}
1765
1836
  * @description Recursively checks for duplicate text style nodes within a given parent node.
1766
1837
  * - If duplicate styles are found, redundant attributes are removed.
1767
1838
  * @param {Node} oNode The node to check for duplicate styles.
1768
1839
  * @param {Node} parentNode The parent node where the duplicate check occurs.
1769
1840
  * @returns {Node} The cleaned node with redundant styles removed.
1770
1841
  */
1771
- _dupleCheck(oNode, parentNode) {
1772
- if (!this.format.isTextStyleNode(oNode)) return;
1842
+ #dupleCheck(oNode, parentNode) {
1843
+ if (!this.#$.format.isTextStyleNode(oNode)) return;
1773
1844
 
1774
- const oStyles = (oNode.style.cssText.match(/[^;]+;/g) || []).map(function (v) {
1845
+ const oStyles = (oNode.style.cssText.match(_RE_CSS_PARTS) || []).map(function (v) {
1775
1846
  return v.trim();
1776
1847
  });
1777
1848
  const nodeName = oNode.nodeName;
1778
- if (/^span$/i.test(nodeName) && oStyles.length === 0) return oNode;
1849
+ if (_RE_SPAN.test(nodeName) && oStyles.length === 0) return oNode;
1779
1850
 
1780
- const inst = this.format;
1851
+ const inst = this.#$.format;
1781
1852
  let duple = false;
1782
1853
  (function recursionFunc(ancestor) {
1783
1854
  if (dom.check.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
1784
1855
  if (ancestor.nodeName === nodeName) {
1785
1856
  duple = true;
1786
- const styles = ancestor.style.cssText.match(/[^;]+;/g) || [];
1857
+ const styles = ancestor.style.cssText.match(_RE_CSS_PARTS) || [];
1787
1858
  for (let i = 0, len = styles.length, j; i < len; i++) {
1788
1859
  if ((j = oStyles.indexOf(styles[i].trim())) > -1) {
1789
1860
  oStyles.splice(j, 1);
@@ -1802,24 +1873,84 @@ HTML.prototype = {
1802
1873
  oNode.setAttribute('style', '');
1803
1874
  oNode.removeAttribute('style');
1804
1875
  }
1805
- if (!oNode.attributes.length) {
1876
+ if (oNode.attributes.length === 0) {
1806
1877
  oNode.setAttribute('data-duple', 'true');
1807
1878
  }
1808
1879
  }
1809
1880
 
1810
1881
  return oNode;
1811
- },
1882
+ }
1883
+
1884
+ /**
1885
+ * @description Tag and tag attribute check RegExp function.
1886
+ * @param {string} m RegExp value
1887
+ * @param {string} t RegExp value
1888
+ * @returns {string}
1889
+ */
1890
+ #CleanElements(attrFilter, styleFilter, m, t) {
1891
+ if (_RE_XML_NS_TAG.test(m)) return m;
1892
+
1893
+ let v = null;
1894
+ const tagName = t.match(_RE_TAG_NAME)[0].toLowerCase();
1895
+
1896
+ if (attrFilter) {
1897
+ // blacklist
1898
+ const bAttr = this.#attributeBlacklist[tagName];
1899
+ m = m.replace(_RE_ON_HANDLER, '');
1900
+ if (bAttr) m = m.replace(bAttr, '');
1901
+ else m = m.replace(this.#attributeBlacklistRegExp, '');
1902
+
1903
+ // whitelist
1904
+ const wAttr = this.#attributeWhitelist[tagName];
1905
+ if (wAttr) v = m.match(wAttr);
1906
+ else v = m.match(this.#attributeWhitelistRegExp);
1907
+ }
1908
+
1909
+ if (!styleFilter) return m;
1910
+
1911
+ // attribute
1912
+ if (tagName === 'a') {
1913
+ const sv = m.match(/(?:(?:id|name)\s*=\s*(?:"|')[^"']*(?:"|'))/g);
1914
+ if (sv) {
1915
+ v ||= [];
1916
+ v.push(sv[0]);
1917
+ }
1918
+ } else if (!v || !_RE_STYLE_EQ.test(v.toString())) {
1919
+ if (this.#textStyleTags.includes(tagName)) {
1920
+ v = this.#cleanStyle(m, v, tagName);
1921
+ } else if (this.#$.format.isLine(tagName)) {
1922
+ v = this.#cleanStyle(m, v, 'line');
1923
+ } else if (this.#cleanStyleTagKeyRegExp.test(tagName)) {
1924
+ v = this.#cleanStyle(m, v, tagName);
1925
+ }
1926
+ }
1927
+
1928
+ // figure
1929
+ if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
1930
+ const sv = m.match(_RE_STYLE_ATTR);
1931
+ v ||= [];
1932
+ if (sv) v.push(sv[0]);
1933
+ }
1934
+
1935
+ if (v) {
1936
+ for (let i = 0, len = v.length, a; i < len; i++) {
1937
+ a = _isSafeAttribute(v[i].trim()) ? v[i] : '';
1938
+ t += (_RE_LEADING_SPACE.test(a) ? '' : ' ') + a;
1939
+ }
1940
+ }
1941
+
1942
+ return t;
1943
+ }
1812
1944
 
1813
1945
  /**
1814
- * @private
1815
- * @this {HTMLThis}
1946
+ * @internal
1816
1947
  * @description Reset autoStyleify options.
1817
1948
  * @param {Array.<string>} autoStyleify Styles applied automatically on text input.
1818
- * - ex ["bold", "underline", "italic", "strike"]
1949
+ * - ex `["bold", "underline", "italic", "strike"]`
1819
1950
  */
1820
1951
  __resetAutoStyleify(autoStyleify) {
1821
1952
  if (autoStyleify.length > 0) {
1822
- const convertTextTags = this.options.get('convertTextTags');
1953
+ const convertTextTags = this.#options.get('convertTextTags');
1823
1954
  const styleToTag = {};
1824
1955
  autoStyleify.forEach((style) => {
1825
1956
  switch (style) {
@@ -1837,80 +1968,68 @@ HTML.prototype = {
1837
1968
  break;
1838
1969
  }
1839
1970
  });
1840
- this._autoStyleify = styleToTag;
1971
+ this.#autoStyleify = styleToTag;
1841
1972
  } else {
1842
- this._autoStyleify = null;
1973
+ this.#autoStyleify = null;
1843
1974
  }
1844
- },
1845
-
1846
- constructor: HTML
1847
- };
1848
-
1849
- /**
1850
- * @private
1851
- * @this {HTMLThis}
1852
- * @description Tag and tag attribute check RegExp function.
1853
- * @param {string} m RegExp value
1854
- * @param {string} t RegExp value
1855
- * @returns {string}
1856
- */
1857
- function CleanElements(attrFilter, styleFilter, m, t) {
1858
- if (/^<[a-z0-9]+:[a-z0-9]+/i.test(m)) return m;
1859
-
1860
- let v = null;
1861
- const tagName = t.match(/(?!<)[a-zA-Z0-9-]+/)[0].toLowerCase();
1862
-
1863
- if (attrFilter) {
1864
- // blacklist
1865
- const bAttr = this._attributeBlacklist[tagName];
1866
- m = m.replace(/\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/gi, '');
1867
- if (bAttr) m = m.replace(bAttr, '');
1868
- else m = m.replace(this._attributeBlacklistRegExp, '');
1869
-
1870
- // whitelist
1871
- const wAttr = this._attributeWhitelist[tagName];
1872
- if (wAttr) v = m.match(wAttr);
1873
- else v = m.match(this._attributeWhitelistRegExp);
1874
1975
  }
1875
1976
 
1876
- if (!styleFilter) return m;
1877
-
1878
- // attribute
1879
- if (tagName === 'a') {
1880
- const sv = m.match(/(?:(?:id|name)\s*=\s*(?:"|')[^"']*(?:"|'))/g);
1881
- if (sv) {
1882
- if (!v) v = [];
1883
- v.push(sv[0]);
1884
- }
1885
- } else if (!v || !/style=/i.test(v.toString())) {
1886
- if (this._textStyleTags.includes(tagName)) {
1887
- v = this._cleanStyle(m, v, tagName);
1888
- } else if (this.format.isLine(tagName)) {
1889
- v = this._cleanStyle(m, v, 'line');
1890
- } else if (this._cleanStyleTagKeyRegExp.test(tagName)) {
1891
- v = this._cleanStyle(m, v, tagName);
1892
- }
1893
- }
1894
-
1895
- // figure
1896
- if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
1897
- const sv = m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/);
1898
- if (!v) v = [];
1899
- if (sv) v.push(sv[0]);
1900
- }
1901
-
1902
- if (v) {
1903
- for (let i = 0, len = v.length, a; i < len; i++) {
1904
- a = /^(?:href|src)\s*=\s*('|"|\s)*javascript\s*:/i.test(v[i].trim()) ? '' : v[i];
1905
- t += (/^\s/.test(a) ? '' : ' ') + a;
1977
+ /**
1978
+ * @internal
1979
+ * @description Destroy the HTML instance and release memory
1980
+ */
1981
+ _destroy() {
1982
+ // Clear Map
1983
+ if (this.#cleanStyleRegExpMap) {
1984
+ this.#cleanStyleRegExpMap.clear();
1906
1985
  }
1907
1986
  }
1987
+ }
1908
1988
 
1909
- return t;
1989
+ /** Module-level regex constants */
1990
+ // #CleanElements
1991
+ const _RE_XML_NS_TAG = /^<[a-z0-9]+:[a-z0-9]+/i;
1992
+ const _RE_TAG_NAME = /(?!<)[a-zA-Z0-9-]+/;
1993
+ const _RE_ON_HANDLER = /\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/gi;
1994
+ const _RE_STYLE_EQ = /style=/i;
1995
+ const _RE_STYLE_ATTR = /style\s*=\s*(?:"|')[^"']*(?:"|')/;
1996
+ const _RE_LEADING_SPACE = /^\s/;
1997
+
1998
+ // #cleanStyle
1999
+ const _RE_TAG_ATTRS = /<[^\s]+\s(.+)/;
2000
+ const _RE_SIZE_ATTR = /\ssize="([^"]+)"/i;
2001
+ const _RE_FACE_ATTR = /\sface="([^"]+)"/i;
2002
+ const _RE_COLOR_ATTR = /\scolor="([^"]+)"/i;
2003
+ const _RE_AMP_QUOT = /&quot;/g;
2004
+ const _RE_CSS_PROP = /([a-zA-Z0-9-]+)(:)([^"']+)/;
2005
+ const _RE_CSS_GLOBAL = /inherit|initial|revert|unset/i;
2006
+ const _RE_DQUOTE = /"/g;
2007
+ const _RE_CSS_VALUE = /:\s*([^;]+)/;
2008
+ const _RE_TRANSPARENT_COLOR = /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i;
2009
+
2010
+ // #dupleCheck
2011
+ const _RE_CSS_PARTS = /[^;]+;/g;
2012
+ const _RE_SPAN = /^span$/i;
2013
+
2014
+ // _isSafeAttribute
2015
+ const _SAFE_URL_PROTOCOL = /^(?:https?|ftps?|mailto|tel|blob|sms|geo|webcal|callto):|^[#/]|^data:image\//i;
2016
+ const _URL_ATTR_PATTERN = /^(?:href|src)\s*=\s*(?:'|"|\s)*/i;
2017
+ const _RE_ATTR_VALUE = /=\s*(?:"|'|)\s*([^"'\s>]*)/;
2018
+ const _RE_WHITESPACE = /[\s\r\n\t]+/g;
2019
+ const _RE_HTML_ENTITY = /&(#x?[0-9a-f]+|[a-z]+);/gi;
2020
+ const _RE_COLON = /:/i;
2021
+
2022
+ function _isSafeAttribute(attr) {
2023
+ if (!_URL_ATTR_PATTERN.test(attr)) return true;
2024
+
2025
+ const urlMatch = attr.match(_RE_ATTR_VALUE);
2026
+ if (!urlMatch) return true;
2027
+
2028
+ const url = urlMatch[1].replace(_RE_WHITESPACE, '').replace(_RE_HTML_ENTITY, '');
2029
+ return _SAFE_URL_PROTOCOL.test(url) || !_RE_COLON.test(url);
1910
2030
  }
1911
2031
 
1912
2032
  /**
1913
- * @private
1914
2033
  * @description Get related list
1915
2034
  * @param {string} str Regular expression string
1916
2035
  * @param {string} str2 Regular expression string