suneditor 3.0.0-alpha.2 → 3.0.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.eslintrc.json +4 -3
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +19 -11
  4. package/README_V3_TEMP.md +705 -0
  5. package/dist/suneditor.min.css +1 -0
  6. package/dist/suneditor.min.js +1 -0
  7. package/example.md +587 -0
  8. package/package.json +15 -9
  9. package/src/assets/icons/_default.js +166 -131
  10. package/src/assets/{suneditor-content.css → suneditor-contents.css} +182 -45
  11. package/src/assets/suneditor.css +1195 -556
  12. package/src/assets/variables.css +138 -0
  13. package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
  14. package/src/core/base/eventHandlers/handler_ww_clipboard.js +29 -4
  15. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +59 -15
  16. package/src/core/base/eventHandlers/handler_ww_key_input.js +426 -212
  17. package/src/core/base/eventHandlers/handler_ww_mouse.js +108 -32
  18. package/src/core/base/eventManager.js +540 -209
  19. package/src/core/base/events.js +616 -320
  20. package/src/core/base/history.js +93 -39
  21. package/src/core/class/char.js +29 -13
  22. package/src/core/class/component.js +332 -145
  23. package/src/core/class/format.js +671 -509
  24. package/src/core/class/html.js +504 -290
  25. package/src/core/class/menu.js +114 -47
  26. package/src/core/class/nodeTransform.js +111 -66
  27. package/src/core/class/offset.js +409 -105
  28. package/src/core/class/selection.js +220 -108
  29. package/src/core/class/shortcuts.js +68 -8
  30. package/src/core/class/toolbar.js +106 -116
  31. package/src/core/class/ui.js +330 -0
  32. package/src/core/class/viewer.js +178 -74
  33. package/src/core/editor.js +489 -384
  34. package/src/core/section/actives.js +118 -22
  35. package/src/core/section/constructor.js +504 -170
  36. package/src/core/section/context.js +28 -23
  37. package/src/core/section/documentType.js +561 -0
  38. package/src/editorInjector/_classes.js +19 -5
  39. package/src/editorInjector/_core.js +71 -7
  40. package/src/editorInjector/index.js +63 -1
  41. package/src/helper/converter.js +137 -19
  42. package/src/helper/dom/domCheck.js +294 -0
  43. package/src/helper/dom/domQuery.js +609 -0
  44. package/src/helper/dom/domUtils.js +533 -0
  45. package/src/helper/dom/index.js +12 -0
  46. package/src/helper/env.js +42 -19
  47. package/src/helper/index.js +7 -4
  48. package/src/helper/keyCodeMap.js +183 -0
  49. package/src/helper/numbers.js +8 -8
  50. package/src/helper/unicode.js +5 -5
  51. package/src/langs/ckb.js +69 -3
  52. package/src/langs/cs.js +67 -1
  53. package/src/langs/da.js +68 -2
  54. package/src/langs/de.js +68 -3
  55. package/src/langs/en.js +29 -1
  56. package/src/langs/es.js +68 -3
  57. package/src/langs/fa.js +70 -2
  58. package/src/langs/fr.js +68 -2
  59. package/src/langs/he.js +68 -3
  60. package/src/langs/hu.js +226 -0
  61. package/src/langs/index.js +3 -2
  62. package/src/langs/it.js +65 -0
  63. package/src/langs/ja.js +68 -3
  64. package/src/langs/ko.js +66 -1
  65. package/src/langs/lv.js +68 -3
  66. package/src/langs/nl.js +68 -3
  67. package/src/langs/pl.js +68 -3
  68. package/src/langs/pt_br.js +65 -0
  69. package/src/langs/ro.js +69 -4
  70. package/src/langs/ru.js +68 -3
  71. package/src/langs/se.js +68 -3
  72. package/src/langs/tr.js +68 -0
  73. package/src/langs/ua.js +68 -3
  74. package/src/langs/ur.js +71 -6
  75. package/src/langs/zh_cn.js +69 -4
  76. package/src/modules/ApiManager.js +77 -54
  77. package/src/modules/Browser.js +667 -0
  78. package/src/modules/ColorPicker.js +162 -102
  79. package/src/modules/Controller.js +233 -136
  80. package/src/modules/Figure.js +913 -489
  81. package/src/modules/FileManager.js +141 -72
  82. package/src/modules/HueSlider.js +113 -61
  83. package/src/modules/Modal.js +292 -113
  84. package/src/modules/ModalAnchorEditor.js +380 -230
  85. package/src/modules/SelectMenu.js +270 -168
  86. package/src/modules/_DragHandle.js +2 -1
  87. package/src/modules/index.js +3 -3
  88. package/src/plugins/browser/audioGallery.js +83 -0
  89. package/src/plugins/browser/fileBrowser.js +103 -0
  90. package/src/plugins/browser/fileGallery.js +83 -0
  91. package/src/plugins/browser/imageGallery.js +81 -0
  92. package/src/plugins/browser/videoGallery.js +103 -0
  93. package/src/plugins/command/blockquote.js +40 -27
  94. package/src/plugins/command/exportPDF.js +134 -0
  95. package/src/plugins/command/fileUpload.js +226 -158
  96. package/src/plugins/command/list_bulleted.js +93 -47
  97. package/src/plugins/command/list_numbered.js +93 -47
  98. package/src/plugins/dropdown/align.js +66 -54
  99. package/src/plugins/dropdown/backgroundColor.js +76 -45
  100. package/src/plugins/dropdown/font.js +71 -47
  101. package/src/plugins/dropdown/fontColor.js +78 -46
  102. package/src/plugins/dropdown/formatBlock.js +74 -33
  103. package/src/plugins/dropdown/hr.js +102 -51
  104. package/src/plugins/dropdown/layout.js +37 -26
  105. package/src/plugins/dropdown/lineHeight.js +54 -38
  106. package/src/plugins/dropdown/list.js +60 -45
  107. package/src/plugins/dropdown/paragraphStyle.js +51 -30
  108. package/src/plugins/dropdown/table.js +1269 -777
  109. package/src/plugins/dropdown/template.js +38 -26
  110. package/src/plugins/dropdown/textStyle.js +43 -31
  111. package/src/plugins/field/mention.js +144 -82
  112. package/src/plugins/index.js +32 -6
  113. package/src/plugins/input/fontSize.js +161 -108
  114. package/src/plugins/input/pageNavigator.js +70 -0
  115. package/src/plugins/modal/audio.js +341 -169
  116. package/src/plugins/modal/drawing.js +530 -0
  117. package/src/plugins/modal/embed.js +886 -0
  118. package/src/plugins/modal/image.js +673 -358
  119. package/src/plugins/modal/link.js +100 -71
  120. package/src/plugins/modal/math.js +384 -168
  121. package/src/plugins/modal/video.js +693 -336
  122. package/src/plugins/popup/anchor.js +222 -0
  123. package/src/suneditor.js +54 -12
  124. package/src/themes/dark.css +85 -0
  125. package/src/typedef.js +86 -0
  126. package/types/assets/icons/_default.d.ts +152 -0
  127. package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
  128. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
  129. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
  130. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
  131. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
  132. package/types/core/base/eventManager.d.ts +377 -0
  133. package/types/core/base/events.d.ts +297 -0
  134. package/types/core/base/history.d.ts +81 -0
  135. package/types/core/class/char.d.ts +60 -0
  136. package/types/core/class/component.d.ts +259 -0
  137. package/types/core/class/format.d.ts +615 -0
  138. package/types/core/class/html.d.ts +377 -0
  139. package/types/core/class/menu.d.ts +118 -0
  140. package/types/core/class/nodeTransform.d.ts +93 -0
  141. package/types/core/class/offset.d.ts +512 -0
  142. package/types/core/class/selection.d.ts +188 -0
  143. package/types/core/class/shortcuts.d.ts +142 -0
  144. package/types/core/class/toolbar.d.ts +189 -0
  145. package/types/core/class/ui.d.ts +144 -0
  146. package/types/core/class/viewer.d.ts +140 -0
  147. package/types/core/editor.d.ts +606 -0
  148. package/types/core/section/actives.d.ts +46 -0
  149. package/types/core/section/constructor.d.ts +748 -0
  150. package/types/core/section/context.d.ts +45 -0
  151. package/types/core/section/documentType.d.ts +178 -0
  152. package/types/editorInjector/_classes.d.ts +41 -0
  153. package/types/editorInjector/_core.d.ts +92 -0
  154. package/types/editorInjector/index.d.ts +71 -0
  155. package/types/helper/converter.d.ts +150 -0
  156. package/types/helper/dom/domCheck.d.ts +182 -0
  157. package/types/helper/dom/domQuery.d.ts +214 -0
  158. package/types/helper/dom/domUtils.d.ts +211 -0
  159. package/types/helper/dom/index.d.ts +9 -0
  160. package/types/helper/env.d.ts +149 -0
  161. package/types/helper/index.d.ts +163 -0
  162. package/types/helper/keyCodeMap.d.ts +110 -0
  163. package/types/helper/numbers.d.ts +43 -0
  164. package/types/helper/unicode.d.ts +28 -0
  165. package/types/index.d.ts +0 -0
  166. package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +170 -103
  167. package/types/langs/ckb.d.ts +384 -0
  168. package/types/langs/cs.d.ts +384 -0
  169. package/types/langs/da.d.ts +384 -0
  170. package/types/langs/de.d.ts +384 -0
  171. package/types/langs/en.d.ts +384 -0
  172. package/types/langs/es.d.ts +384 -0
  173. package/types/langs/fa.d.ts +384 -0
  174. package/types/langs/fr.d.ts +384 -0
  175. package/types/langs/he.d.ts +384 -0
  176. package/types/langs/hu.d.ts +384 -0
  177. package/types/langs/index.d.ts +48 -0
  178. package/types/langs/it.d.ts +384 -0
  179. package/types/langs/ja.d.ts +384 -0
  180. package/types/langs/ko.d.ts +384 -0
  181. package/types/langs/lv.d.ts +384 -0
  182. package/types/langs/nl.d.ts +384 -0
  183. package/types/langs/pl.d.ts +384 -0
  184. package/types/langs/pt_br.d.ts +384 -0
  185. package/types/langs/ro.d.ts +384 -0
  186. package/types/langs/ru.d.ts +384 -0
  187. package/types/langs/se.d.ts +384 -0
  188. package/types/langs/tr.d.ts +384 -0
  189. package/types/langs/ua.d.ts +384 -0
  190. package/types/langs/ur.d.ts +384 -0
  191. package/types/langs/zh_cn.d.ts +384 -0
  192. package/types/modules/ApiManager.d.ts +125 -0
  193. package/types/modules/Browser.d.ts +326 -0
  194. package/types/modules/ColorPicker.d.ts +131 -0
  195. package/types/modules/Controller.d.ts +231 -0
  196. package/types/modules/Figure.d.ts +504 -0
  197. package/types/modules/FileManager.d.ts +202 -0
  198. package/types/modules/HueSlider.d.ts +136 -0
  199. package/types/modules/Modal.d.ts +117 -0
  200. package/types/modules/ModalAnchorEditor.d.ts +236 -0
  201. package/types/modules/SelectMenu.d.ts +194 -0
  202. package/types/modules/_DragHandle.d.ts +7 -0
  203. package/types/modules/index.d.ts +26 -0
  204. package/types/plugins/browser/audioGallery.d.ts +55 -0
  205. package/types/plugins/browser/fileBrowser.d.ts +64 -0
  206. package/types/plugins/browser/fileGallery.d.ts +55 -0
  207. package/types/plugins/browser/imageGallery.d.ts +51 -0
  208. package/types/plugins/browser/videoGallery.d.ts +57 -0
  209. package/types/plugins/command/blockquote.d.ts +28 -0
  210. package/types/plugins/command/exportPDF.d.ts +46 -0
  211. package/types/plugins/command/fileUpload.d.ts +156 -0
  212. package/types/plugins/command/list_bulleted.d.ts +56 -0
  213. package/types/plugins/command/list_numbered.d.ts +56 -0
  214. package/types/plugins/dropdown/align.d.ts +60 -0
  215. package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
  216. package/types/plugins/dropdown/font.d.ts +54 -0
  217. package/types/plugins/dropdown/fontColor.d.ts +63 -0
  218. package/types/plugins/dropdown/formatBlock.d.ts +58 -0
  219. package/types/plugins/dropdown/hr.d.ts +81 -0
  220. package/types/plugins/dropdown/layout.d.ts +40 -0
  221. package/types/plugins/dropdown/lineHeight.d.ts +50 -0
  222. package/types/plugins/dropdown/list.d.ts +39 -0
  223. package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
  224. package/types/plugins/dropdown/table.d.ts +579 -0
  225. package/types/plugins/dropdown/template.d.ts +40 -0
  226. package/types/plugins/dropdown/textStyle.d.ts +41 -0
  227. package/types/plugins/field/mention.d.ts +102 -0
  228. package/types/plugins/index.d.ts +107 -0
  229. package/types/plugins/input/fontSize.d.ts +170 -0
  230. package/types/plugins/input/pageNavigator.d.ts +28 -0
  231. package/types/plugins/modal/audio.d.ts +269 -0
  232. package/types/plugins/modal/drawing.d.ts +246 -0
  233. package/types/plugins/modal/embed.d.ts +387 -0
  234. package/types/plugins/modal/image.d.ts +451 -0
  235. package/types/plugins/modal/link.d.ts +128 -0
  236. package/types/plugins/modal/math.d.ts +193 -0
  237. package/types/plugins/modal/video.d.ts +485 -0
  238. package/types/plugins/popup/anchor.d.ts +56 -0
  239. package/types/suneditor.d.ts +51 -0
  240. package/types/typedef-global.d.ts +144 -0
  241. package/src/core/class/notice.js +0 -42
  242. package/src/helper/domUtils.js +0 -1177
  243. package/src/modules/FileBrowser.js +0 -271
  244. package/src/plugins/command/exportPdf.js +0 -168
  245. package/src/plugins/fileBrowser/imageGallery.js +0 -81
  246. package/src/themes/test.css +0 -61
  247. package/typings/CommandPlugin.d.ts +0 -8
  248. package/typings/DialogPlugin.d.ts +0 -20
  249. package/typings/FileBrowserPlugin.d.ts +0 -30
  250. package/typings/Module.d.ts +0 -15
  251. package/typings/Plugin.d.ts +0 -42
  252. package/typings/SubmenuPlugin.d.ts +0 -8
  253. package/typings/_classes.d.ts +0 -17
  254. package/typings/_colorPicker.d.ts +0 -60
  255. package/typings/_core.d.ts +0 -55
  256. package/typings/align.d.ts +0 -5
  257. package/typings/audio.d.ts +0 -5
  258. package/typings/backgroundColor.d.ts +0 -5
  259. package/typings/blockquote.d.ts +0 -5
  260. package/typings/char.d.ts +0 -39
  261. package/typings/component.d.ts +0 -38
  262. package/typings/context.d.ts +0 -39
  263. package/typings/converter.d.ts +0 -33
  264. package/typings/dialog.d.ts +0 -28
  265. package/typings/domUtils.d.ts +0 -361
  266. package/typings/editor.d.ts +0 -7
  267. package/typings/editor.ts +0 -542
  268. package/typings/env.d.ts +0 -70
  269. package/typings/eventManager.d.ts +0 -37
  270. package/typings/events.d.ts +0 -262
  271. package/typings/fileBrowser.d.ts +0 -42
  272. package/typings/fileManager.d.ts +0 -67
  273. package/typings/font.d.ts +0 -5
  274. package/typings/fontColor.d.ts +0 -5
  275. package/typings/fontSize.d.ts +0 -5
  276. package/typings/format.d.ts +0 -191
  277. package/typings/formatBlock.d.ts +0 -5
  278. package/typings/history.d.ts +0 -48
  279. package/typings/horizontalRule.d.ts +0 -5
  280. package/typings/image.d.ts +0 -5
  281. package/typings/imageGallery.d.ts +0 -5
  282. package/typings/index.d.ts +0 -21
  283. package/typings/index.modules.d.ts +0 -11
  284. package/typings/index.plugins.d.ts +0 -58
  285. package/typings/lineHeight.d.ts +0 -5
  286. package/typings/link.d.ts +0 -5
  287. package/typings/list.d.ts +0 -5
  288. package/typings/math.d.ts +0 -5
  289. package/typings/mediaContainer.d.ts +0 -25
  290. package/typings/mention.d.ts +0 -5
  291. package/typings/node.d.ts +0 -57
  292. package/typings/notice.d.ts +0 -16
  293. package/typings/numbers.d.ts +0 -29
  294. package/typings/offset.d.ts +0 -24
  295. package/typings/options.d.ts +0 -589
  296. package/typings/paragraphStyle.d.ts +0 -5
  297. package/typings/resizing.d.ts +0 -141
  298. package/typings/selection.d.ts +0 -94
  299. package/typings/shortcuts.d.ts +0 -13
  300. package/typings/suneditor.d.ts +0 -9
  301. package/typings/table.d.ts +0 -5
  302. package/typings/template.d.ts +0 -5
  303. package/typings/textStyle.d.ts +0 -5
  304. package/typings/toolbar.d.ts +0 -32
  305. package/typings/unicode.d.ts +0 -25
  306. package/typings/video.d.ts +0 -5
@@ -3,29 +3,51 @@
3
3
  */
4
4
 
5
5
  import CoreInjector from '../../editorInjector/_core';
6
- import { domUtils, converter, numbers, unicode, env } from '../../helper';
6
+ import { dom, converter, numbers, unicode, env } from '../../helper';
7
7
 
8
8
  const REQUIRED_DATA_ATTRS = 'data-se-[^\\s]+';
9
+ const V2_MIG_DATA_ATTRS = '|data-index|data-file-size|data-file-name|data-exp|data-font-size';
9
10
 
10
- const HTML = function (editor) {
11
+ /**
12
+ * @typedef {Omit<HTML & Partial<__se__EditorInjector>, 'html'>} HTMLThis
13
+ */
14
+
15
+ /**
16
+ * @constructor
17
+ * @this {HTMLThis}
18
+ * @description All HTML related classes involved in the editing area
19
+ * @param {__se__EditorCore} editor - The root editor instance
20
+ */
21
+ function HTML(editor) {
11
22
  CoreInjector.call(this, editor);
23
+ const options = this.options;
12
24
 
13
25
  // members
14
- const options = this.options;
26
+ this.fontSizeUnitRegExp = null;
27
+
15
28
  this._isAllowedClassName = function (v) {
16
29
  return this.test(v) ? v : '';
17
- }.bind(this.options.get('allowedClassName'));
30
+ }.bind(options.get('allowedClassName'));
18
31
  this._allowHTMLComment = null;
19
32
  this._disallowedStyleNodesRegExp = null;
20
33
  this._htmlCheckWhitelistRegExp = null;
21
34
  this._htmlCheckBlacklistRegExp = null;
22
35
  this._elementWhitelistRegExp = null;
23
36
  this._elementBlacklistRegExp = null;
37
+ /** @type {Object<string, RegExp>} */
24
38
  this._attributeWhitelist = null;
25
- this._attributeWhitelistRegExp = null;
39
+ /** @type {Object<string, RegExp>} */
26
40
  this._attributeBlacklist = null;
41
+ this._attributeWhitelistRegExp = null;
27
42
  this._attributeBlacklistRegExp = null;
43
+ this._cleanStyleTagKeyRegExp = null;
44
+ this._cleanStyleRegExpMap = null;
28
45
  this._textStyleTags = options.get('_textStyleTags');
46
+ /** @type {Object<string, *>} */
47
+ this._autoStyleify = null;
48
+ this.__disallowedTagsRegExp = null;
49
+ this.__disallowedTagNameRegExp = null;
50
+ this.__allowedTagNameRegExp = null;
29
51
 
30
52
  // clean styles
31
53
  const tagStyles = options.get('tagStyles');
@@ -59,7 +81,7 @@ const HTML = function (editor) {
59
81
  this._cleanStyleRegExpMap = stylesMap;
60
82
 
61
83
  // font size unit
62
- this.fontSizeUnitRegExp = new RegExp('\\d+(' + this.options.get('fontSizeUnits').join('|') + ')$', 'i');
84
+ this.fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
63
85
 
64
86
  // extra tags
65
87
  const allowedExtraTags = options.get('_allowedExtraTag');
@@ -94,6 +116,8 @@ const HTML = function (editor) {
94
116
  // attributes
95
117
  const regEndStr = '\\s*=\\s*(")[^"]*\\1';
96
118
  const _wAttr = options.get('attributeWhitelist');
119
+
120
+ /** @type {Object<string, RegExp>} */
97
121
  let tagsAttr = {};
98
122
  let allAttr = '';
99
123
  if (_wAttr) {
@@ -107,7 +131,7 @@ const HTML = function (editor) {
107
131
  }
108
132
  }
109
133
 
110
- this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + ')' + regEndStr, 'ig');
134
+ this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
111
135
  this._attributeWhitelist = tagsAttr;
112
136
 
113
137
  // blacklist
@@ -126,63 +150,158 @@ const HTML = function (editor) {
126
150
 
127
151
  this._attributeBlacklistRegExp = new RegExp('\\s(?:' + (allAttr || '^') + ')' + regEndStr, 'ig');
128
152
  this._attributeBlacklist = tagsAttr;
129
- };
153
+
154
+ // autoStyleify
155
+ if (options.get('autoStyleify').length > 0) {
156
+ const convertTextTags = options.get('convertTextTags');
157
+ const styleToTag = {};
158
+ options.get('autoStyleify').forEach((style) => {
159
+ switch (style) {
160
+ case 'bold':
161
+ styleToTag.bold = { regex: /font-weight\s*:\s*bold/i, tag: convertTextTags.bold };
162
+ break;
163
+ case 'italic':
164
+ styleToTag.italic = { regex: /font-style\s*:\s*italic/i, tag: convertTextTags.italic };
165
+ break;
166
+ case 'underline':
167
+ styleToTag.underline = { regex: /text-decoration\s*:\s*underline/i, tag: convertTextTags.underline };
168
+ break;
169
+ case 'strike':
170
+ styleToTag.strike = { regex: /text-decoration\s*:\s*line-through/i, tag: convertTextTags.strike };
171
+ break;
172
+ }
173
+ });
174
+ this._autoStyleify = styleToTag;
175
+ }
176
+ }
130
177
 
131
178
  HTML.prototype = {
132
179
  /**
133
- * @description Clean and compress the HTML code to suit the editor format.
134
- * @param {string} html HTML string
135
- * @param {boolean} requireFormat If true, text nodes that do not have a format node is wrapped with the format tag.
136
- * @param {string|RegExp|null} whitelist Regular expression of allowed tags.
137
- * RegExp object is create by helper.converter.createElementWhitelist method.
138
- * @param {string|RegExp|null} blacklist Regular expression of disallowed tags.
139
- * RegExp object is create by helper.converter.createElementBlacklist method.
140
- * @returns {string}
180
+ * @this {HTMLThis}
181
+ * @description Filters an HTML string based on allowed and disallowed tags, with optional custom validation.
182
+ * - Removes blacklisted tags and keeps only whitelisted tags.
183
+ * - Allows custom validation functions to replace, modify, or remove elements.
184
+ * @param {string} html - The HTML string to be filtered.
185
+ * @param {Object} params - Filtering parameters.
186
+ * @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by '|'. (e.g. "div|p|span").
187
+ * @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by '|'. (e.g. "script|iframe").
188
+ * @param {(node: Node) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
189
+ * - Return `null` to remove the node.
190
+ * - Return a `Node` to replace the current node.
191
+ * - Return a `string` to replace the node's outerHTML.
192
+ * @param {boolean} [params.validateAll] - Whether to apply validation to all nodes.
193
+ * @returns {string} - The filtered HTML string.
194
+ */
195
+ filter(html, { tagWhitelist, tagBlacklist, validate, validateAll }) {
196
+ if (tagWhitelist) {
197
+ html = html.replace(converter.createElementWhitelist(tagWhitelist), '');
198
+ }
199
+ if (tagBlacklist) {
200
+ html = html.replace(converter.createElementBlacklist(tagBlacklist), '');
201
+ }
202
+ if (validate) {
203
+ const parseDocument = new DOMParser().parseFromString(html, 'text/html');
204
+ parseDocument.body.querySelectorAll('*').forEach((node) => {
205
+ if (!node.closest('.se-component') && !node.closest('.se-flex-component')) {
206
+ const result = validate(node);
207
+ if (result === null) {
208
+ node.remove();
209
+ } else if (result instanceof Node) {
210
+ node.replaceWith(result);
211
+ } else if (typeof result === 'string') {
212
+ node.outerHTML = result;
213
+ }
214
+ }
215
+ });
216
+ html = parseDocument.body.innerHTML;
217
+ } else if (validateAll) {
218
+ const parseDocument = new DOMParser().parseFromString(html, 'text/html');
219
+ const compClass = ['.se-component', '.se-flex-component'];
220
+ const closestAny = (element) => compClass.some((selector) => element.closest(selector));
221
+ parseDocument.body.querySelectorAll('*').forEach((node) => {
222
+ if (!closestAny(node)) {
223
+ const result = validate(node);
224
+ if (result === null) {
225
+ node.remove();
226
+ } else if (result instanceof Node) {
227
+ node.replaceWith(result);
228
+ } else if (typeof result === 'string') {
229
+ node.outerHTML = result;
230
+ }
231
+ }
232
+ });
233
+ html = parseDocument.body.innerHTML;
234
+ }
235
+
236
+ return html;
237
+ },
238
+
239
+ /**
240
+ * @this {HTMLThis}
241
+ * @description Cleans and compresses HTML code to suit the editor format.
242
+ * @param {string} html HTML string to clean and compress
243
+ * @param {Object} [options] Cleaning options
244
+ * @param {boolean} [options.forceFormat=false] If true, wraps text nodes without a format node in the format tag.
245
+ * @param {string|RegExp|null} [options.whitelist=null] Regular expression of allowed tags.
246
+ * Create RegExp object using helper.converter.createElementWhitelist method.
247
+ * @param {string|RegExp|null} [options.blacklist=null] Regular expression of disallowed tags.
248
+ * Create RegExp object using helper.converter.createElementBlacklist method.
249
+ * @param {boolean} [options._freeCodeViewMode=false] If true, the free code view mode is enabled.
250
+ * @returns {string} Cleaned and compressed HTML string
141
251
  */
142
- clean(html, requireFormat, whitelist, blacklist) {
252
+ clean(html, { forceFormat, whitelist, blacklist, _freeCodeViewMode } = {}) {
143
253
  const { tagFilter, formatFilter, classFilter, styleNodeFilter, attrFilter, styleFilter } = this.options.get('strictMode');
144
254
  let cleanData = '';
145
255
 
146
256
  html = this.compress(html);
147
257
 
148
258
  if (tagFilter) {
259
+ html = html.replace(this.__disallowedTagsRegExp, '');
149
260
  html = this._deleteDisallowedTags(html, this._elementWhitelistRegExp, this._elementBlacklistRegExp).replace(/<br\/?>$/i, '');
150
261
  }
151
262
 
263
+ if (this._autoStyleify) {
264
+ const domParser = new DOMParser().parseFromString(html, 'text/html');
265
+ dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this._autoStyleify));
266
+ html = domParser.body.innerHTML;
267
+ }
268
+
152
269
  if (attrFilter || styleFilter) {
153
270
  html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, CleanElements.bind(this, attrFilter, styleFilter));
154
271
  }
155
272
 
156
273
  // get dom tree
157
- const dom = this._d.createRange().createContextualFragment(html, true);
274
+ const domParser = this._d.createRange().createContextualFragment(html);
158
275
 
159
276
  if (tagFilter) {
160
277
  try {
161
- this._consistencyCheckOfHTML(dom, this._htmlCheckWhitelistRegExp, this._htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter);
278
+ this._consistencyCheckOfHTML(domParser, this._htmlCheckWhitelistRegExp, this._htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
162
279
  } catch (error) {
163
280
  console.warn('[SUNEDITOR.html.clean.fail]', error.message);
164
281
  }
165
282
  }
166
283
 
167
- this.editor._MELInfo.forEach((method, query) => {
168
- const infoLst = dom.querySelectorAll(query);
169
- for (let i = 0, len = infoLst.length; i < len; i++) {
170
- method(infoLst[i]);
171
- }
172
- });
284
+ if (this.options.get('__pluginRetainFilter')) {
285
+ this.editor._MELInfo.forEach((method, query) => {
286
+ const infoLst = domParser.querySelectorAll(query);
287
+ for (let i = 0, len = infoLst.length; i < len; i++) {
288
+ method(infoLst[i]);
289
+ }
290
+ });
291
+ }
173
292
 
174
293
  if (formatFilter) {
175
- let domTree = dom.childNodes;
176
- if (!requireFormat) requireFormat = this._isFormatData(domTree);
177
- if (requireFormat) domTree = this._editFormat(dom).childNodes;
294
+ let domTree = domParser.childNodes;
295
+ if (!forceFormat) forceFormat = this._isFormatData(domTree);
296
+ if (forceFormat) domTree = this._editFormat(domParser).childNodes;
178
297
 
179
298
  for (let i = 0, len = domTree.length, t; i < len; i++) {
180
299
  t = domTree[i];
181
300
  if (this.__allowedTagNameRegExp.test(t.nodeName)) {
182
- cleanData += t.outerHTML;
301
+ cleanData += /** @type {HTMLElement} */ (t).outerHTML;
183
302
  continue;
184
303
  }
185
- cleanData += this._makeLine(t, requireFormat);
304
+ cleanData += this._makeLine(t, forceFormat);
186
305
  }
187
306
  }
188
307
 
@@ -203,29 +322,32 @@ HTML.prototype = {
203
322
  },
204
323
 
205
324
  /**
206
- * @description Insert an (HTML element / HTML string / plain string) at selection range.
207
- * If "frameOptions.get('charCounter_max')" is exceeded when "html" is added, null is returned without addition.
208
- * @param {Element|String} html HTML Element or HTML string or plain string
209
- * @param {boolean} rangeSelection If true, range select the inserted node.
210
- * @param {boolean} notCheckCharCount If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
211
- * @param {boolean} notCleanData If true, inserts the HTML string without refining it with html.clean.
325
+ * @this {HTMLThis}
326
+ * @description Inserts an (HTML element / HTML string / plain string) at the selection range.
327
+ * - If "frameOptions.get('charCounter_max')" is exceeded when "html" is added, null is returned without addition.
328
+ * @param {Node|string} html HTML Element or HTML string or plain string
329
+ * @param {Object} [options] Options
330
+ * @param {boolean} [options.selectInserted=false] If true, selects the range of the inserted node.
331
+ * @param {boolean} [options.skipCharCount=false] If true, inserts even if "frameOptions.get('charCounter_max')" is exceeded.
332
+ * @param {boolean} [options.skipCleaning=false] If true, inserts the HTML string without refining it with html.clean.
333
+ * @returns {HTMLElement|null} The inserted element or null if insertion failed
212
334
  */
213
- insert(html, rangeSelection, notCheckCharCount, notCleanData) {
214
- if (!this.editor.frameContext.get('wysiwygFrame').contains(this.selection.get().focusNode)) this.editor.focus();
335
+ insert(html, { selectInserted, skipCharCount, skipCleaning } = {}) {
336
+ if (!this.editor.frameContext.get('wysiwyg').contains(this.selection.get().focusNode)) this.editor.focus();
215
337
 
216
338
  if (typeof html === 'string') {
217
- if (!notCleanData) html = this.clean(html, false, null, null);
339
+ if (!skipCleaning) html = this.clean(html, { forceFormat: false, whitelist: null, blacklist: null });
218
340
  try {
219
- if (domUtils.isListCell(this.format.getLine(this.selection.getNode(), null))) {
220
- const dom = this._d.createRange().createContextualFragment(html);
221
- const domTree = dom.childNodes;
341
+ if (dom.check.isListCell(this.format.getLine(this.selection.getNode(), null))) {
342
+ const domParser = this._d.createRange().createContextualFragment(html);
343
+ const domTree = domParser.childNodes;
222
344
  if (this._isFormatData(domTree)) html = this._convertListCell(domTree);
223
345
  }
224
346
 
225
- const dom = this._d.createRange().createContextualFragment(html);
226
- const domTree = dom.childNodes;
347
+ const domParser = this._d.createRange().createContextualFragment(html);
348
+ const domTree = domParser.childNodes;
227
349
 
228
- if (!notCheckCharCount) {
350
+ if (!skipCharCount) {
229
351
  const type = this.editor.frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
230
352
  let checkHTML = '';
231
353
  for (let i = 0, len = domTree.length; i < len; i++) {
@@ -236,12 +358,12 @@ HTML.prototype = {
236
358
 
237
359
  let c, a, t, prev, firstCon;
238
360
  while ((c = domTree[0])) {
239
- if (prev?.nodeType === 3 && a?.nodeType === 1 && domUtils.isBreak(c)) {
361
+ if (prev?.nodeType === 3 && a?.nodeType === 1 && dom.check.isBreak(c)) {
240
362
  prev = c;
241
- domUtils.removeItem(c);
363
+ dom.utils.removeItem(c);
242
364
  continue;
243
365
  }
244
- t = this.insertNode(c, a, true);
366
+ t = this.insertNode(c, { afterNode: a, skipCharCount: true });
245
367
  a = t.container || t;
246
368
  if (!firstCon) firstCon = t;
247
369
  prev = c;
@@ -250,7 +372,7 @@ HTML.prototype = {
250
372
  if (prev?.nodeType === 3 && a?.nodeType === 1) a = prev;
251
373
  const offset = a.nodeType === 3 ? t.endOffset || a.textContent.length : a.childNodes.length;
252
374
 
253
- if (rangeSelection) {
375
+ if (selectInserted) {
254
376
  this.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
255
377
  } else if (!this.component.is(a)) {
256
378
  this.selection.setRange(a, offset, a, offset);
@@ -261,13 +383,13 @@ HTML.prototype = {
261
383
  }
262
384
  } else {
263
385
  if (this.component.is(html)) {
264
- this.component.insert(html, notCheckCharCount, false);
386
+ this.component.insert(html, { skipCharCount, skipSelection: false, skipHistory: false });
265
387
  } else {
266
388
  let afterNode = null;
267
- if (this.format.isLine(html) || domUtils.isMedia(html)) {
389
+ if (this.format.isLine(html) || dom.check.isMedia(html)) {
268
390
  afterNode = this.format.getLine(this.selection.getNode(), null);
269
391
  }
270
- this.insertNode(html, afterNode, notCheckCharCount);
392
+ this.insertNode(html, { afterNode, skipCharCount });
271
393
  }
272
394
  }
273
395
 
@@ -277,23 +399,26 @@ HTML.prototype = {
277
399
  },
278
400
 
279
401
  /**
402
+ * @this {HTMLThis}
280
403
  * @description Delete selected node and insert argument value node and return.
281
- * If the "afterNode" exists, it is inserted after the "afterNode"
282
- * Inserting a text node merges with both text nodes on both sides and returns a new "{ container, startOffset, endOffset }".
404
+ * - If the "afterNode" exists, it is inserted after the "afterNode"
405
+ * - Inserting a text node merges with both text nodes on both sides and returns a new "{ container, startOffset, endOffset }".
283
406
  * @param {Node} oNode Node to be inserted
284
- * @param {Node|null} afterNode If the node exists, it is inserted after the node
285
- * @param {boolean|null} notCheckCharCount If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
407
+ * @param {Object} [options] Options
408
+ * @param {Node} [options.afterNode=null] If the node exists, it is inserted after the node
409
+ * @param {boolean} [options.skipCharCount=null] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
286
410
  * @returns {Object|Node|null}
287
411
  */
288
- insertNode(oNode, afterNode, notCheckCharCount) {
412
+ insertNode(oNode, { afterNode, skipCharCount } = {}) {
289
413
  let result = null;
290
- if (this.editor.frameContext.get('isReadOnly') || (!notCheckCharCount && !this.char.check(oNode, null))) {
414
+ if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(oNode))) {
291
415
  return result;
292
416
  }
293
417
 
418
+ let fNode = null;
294
419
  let range = this.selection.getRange();
295
- let line = domUtils.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.format.getLine(this.selection.getNode(), null);
296
- let insertListCell = domUtils.isListCell(line) && (domUtils.isListCell(oNode) || domUtils.isList(oNode));
420
+ let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.format.getLine(this.selection.getNode(), null);
421
+ let insertListCell = dom.check.isListCell(line) && (dom.check.isListCell(oNode) || dom.check.isList(oNode));
297
422
 
298
423
  let parentNode,
299
424
  originAfter,
@@ -303,12 +428,12 @@ HTML.prototype = {
303
428
  const isFormats = (!freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) || this.component.isBasic(oNode);
304
429
 
305
430
  if (insertListCell) {
306
- tempAfterNode = afterNode || domUtils.isList(oNode) ? line.lastChild : line.nextElementSibling;
307
- tempParentNode = domUtils.isList(oNode) ? line : (tempAfterNode || line).parentNode;
431
+ tempAfterNode = afterNode || dom.check.isList(oNode) ? line.lastChild : line.nextElementSibling;
432
+ tempParentNode = dom.check.isList(oNode) ? line : (tempAfterNode || line).parentNode;
308
433
  }
309
434
 
310
- if (!afterNode && (isFormats || this.component.isBasic(oNode) || domUtils.isMedia(oNode))) {
311
- const isEdge = domUtils.isEdgePoint(range.endContainer, range.endOffset, 'end');
435
+ if (!afterNode && (isFormats || this.component.isBasic(oNode) || dom.check.isMedia(oNode))) {
436
+ const isEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
312
437
  const r = this.remove();
313
438
  const container = r.container;
314
439
  const prevContainer = container === r.prevContainer && range.collapsed ? null : r.prevContainer;
@@ -326,30 +451,30 @@ HTML.prototype = {
326
451
  } else {
327
452
  tempAfterNode = null;
328
453
  }
329
- } else if (insertListCell && domUtils.isListCell(container) && !line.parentElement) {
330
- line = domUtils.createElement('LI');
454
+ } else if (insertListCell && dom.check.isListCell(container) && !line.parentElement) {
455
+ line = dom.utils.createElement('LI');
331
456
  tempParentNode.appendChild(line);
332
457
  container.appendChild(tempParentNode);
333
458
  tempAfterNode = null;
334
- } else if (container.nodeType === 3 || domUtils.isBreak(container) || insertListCell) {
335
- const depthFormat = domUtils.getParentElement(container, (current) => {
336
- return this.format.isBlock(current) || domUtils.isListCell(current);
459
+ } else if (container.nodeType === 3 || dom.check.isBreak(container) || insertListCell) {
460
+ const depthFormat = dom.query.getParentElement(container, (current) => {
461
+ return this.format.isBlock(current) || dom.check.isListCell(current);
337
462
  });
338
- afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 : domUtils.getNodeDepth(depthFormat) + 1);
463
+ afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
339
464
  if (!afterNode) {
340
465
  tempAfterNode = afterNode = line;
341
466
  } else if (insertListCell) {
342
467
  if (line.contains(container)) {
343
- const subList = domUtils.isList(line.lastElementChild);
468
+ const subList = dom.check.isList(line.lastElementChild);
344
469
  let newCell = null;
345
470
  if (!isEdge) {
346
471
  newCell = line.cloneNode(false);
347
- newCell.appendChild(afterNode.textContent.trim() ? afterNode : domUtils.createTextNode(unicode.zeroWidthSpace));
472
+ newCell.appendChild(afterNode.textContent.trim() ? afterNode : dom.utils.createTextNode(unicode.zeroWidthSpace));
348
473
  }
349
474
  if (subList) {
350
475
  if (!newCell) {
351
476
  newCell = line.cloneNode(false);
352
- newCell.appendChild(domUtils.createTextNode(unicode.zeroWidthSpace));
477
+ newCell.appendChild(dom.utils.createTextNode(unicode.zeroWidthSpace));
353
478
  }
354
479
  newCell.appendChild(line.lastElementChild);
355
480
  }
@@ -382,18 +507,18 @@ HTML.prototype = {
382
507
  /** No Select range node */
383
508
  if (range.collapsed) {
384
509
  if (commonCon.nodeType === 3) {
385
- if (commonCon.textContent.length > endOff) afterNode = commonCon.splitText(endOff);
510
+ if (commonCon.textContent.length > endOff) afterNode = /** @type {Text} */ (commonCon).splitText(endOff);
386
511
  else afterNode = commonCon.nextSibling;
387
512
  } else {
388
- if (!domUtils.isBreak(parentNode)) {
513
+ if (!dom.check.isBreak(parentNode)) {
389
514
  const c = parentNode.childNodes[startOff];
390
- const focusNode = c?.nodeType === 3 && domUtils.isZeroWith(c) && domUtils.isBreak(c.nextSibling) ? c.nextSibling : c;
515
+ const focusNode = c?.nodeType === 3 && dom.check.isZeroWidth(c) && dom.check.isBreak(c.nextSibling) ? c.nextSibling : c;
391
516
  if (focusNode) {
392
- if (!focusNode.nextSibling && domUtils.isBreak(focusNode)) {
517
+ if (!focusNode.nextSibling && dom.check.isBreak(focusNode)) {
393
518
  parentNode.removeChild(focusNode);
394
519
  afterNode = null;
395
520
  } else {
396
- afterNode = domUtils.isBreak(focusNode) && !domUtils.isBreak(oNode) ? focusNode : focusNode.nextSibling;
521
+ afterNode = dom.check.isBreak(focusNode) && !dom.check.isBreak(oNode) ? focusNode : focusNode.nextSibling;
397
522
  }
398
523
  } else {
399
524
  afterNode = null;
@@ -408,15 +533,15 @@ HTML.prototype = {
408
533
  const isSameContainer = startCon === endCon;
409
534
 
410
535
  if (isSameContainer) {
411
- if (domUtils.isEdgePoint(endCon, endOff)) afterNode = endCon.nextSibling;
412
- else afterNode = endCon.splitText(endOff);
536
+ if (dom.check.isEdgePoint(endCon, endOff)) afterNode = endCon.nextSibling;
537
+ else afterNode = /** @type {Text} */ (endCon).splitText(endOff);
413
538
 
414
539
  let removeNode = startCon;
415
- if (!domUtils.isEdgePoint(startCon, startOff)) removeNode = startCon.splitText(startOff);
540
+ if (!dom.check.isEdgePoint(startCon, startOff)) removeNode = /** @type {Text} */ (startCon).splitText(startOff);
416
541
 
417
542
  parentNode.removeChild(removeNode);
418
543
  if (parentNode.childNodes.length === 0 && isFormats) {
419
- parentNode.innerHTML = '<br>';
544
+ /** @type {HTMLElement} */ (parentNode).innerHTML = '<br>';
420
545
  }
421
546
  } else {
422
547
  const removedTag = this.remove();
@@ -431,7 +556,7 @@ HTML.prototype = {
431
556
  }
432
557
  }
433
558
 
434
- if (domUtils.isListCell(container) && oNode.nodeType === 3) {
559
+ if (dom.check.isListCell(container) && oNode.nodeType === 3) {
435
560
  parentNode = container;
436
561
  afterNode = null;
437
562
  } else if (!isFormats && prevContainer) {
@@ -447,8 +572,8 @@ HTML.prototype = {
447
572
  } else {
448
573
  afterNode = null;
449
574
  }
450
- } else if (domUtils.isWysiwygFrame(container) && !this.format.isLine(oNode)) {
451
- parentNode = container.appendChild(domUtils.createElement(this.options.get('defaultLine')));
575
+ } else if (dom.check.isWysiwygFrame(container) && !this.format.isLine(oNode)) {
576
+ parentNode = container.appendChild(dom.utils.createElement(this.options.get('defaultLine')));
452
577
  afterNode = null;
453
578
  } else {
454
579
  afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
@@ -472,37 +597,38 @@ HTML.prototype = {
472
597
  // set node
473
598
  const wysiwyg = this.editor.frameContext.get('wysiwyg');
474
599
  if (!insertListCell) {
475
- if (domUtils.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
600
+ if (dom.check.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
476
601
  parentNode = wysiwyg;
477
602
  afterNode = null;
478
603
  }
479
604
 
480
- if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!domUtils.isListCell(parentNode) && this.component.isBasic(oNode))) {
605
+ if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.component.isBasic(oNode))) {
481
606
  const oldParent = parentNode;
482
- if (domUtils.isList(afterNode)) {
607
+ if (dom.check.isList(afterNode)) {
483
608
  parentNode = afterNode;
484
609
  afterNode = null;
485
- } else if (domUtils.isListCell(afterNode)) {
610
+ } else if (dom.check.isListCell(afterNode)) {
486
611
  parentNode = afterNode.previousElementSibling || afterNode;
487
612
  } else if (!originAfter && !afterNode) {
488
613
  const r = this.remove();
489
- const container = r.container.nodeType === 3 ? (domUtils.isListCell(this.format.getLine(r.container, null)) ? r.container : this.format.getLine(r.container, null) || r.container.parentNode) : r.container;
490
- const rangeCon = domUtils.isWysiwygFrame(container) || this.format.isBlock(container);
614
+ 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;
615
+ const rangeCon = dom.check.isWysiwygFrame(container) || this.format.isBlock(container);
491
616
  parentNode = rangeCon ? container : container.parentNode;
492
617
  afterNode = rangeCon ? null : container.nextSibling;
493
618
  }
494
619
 
495
- if (oldParent.childNodes.length === 0 && parentNode !== oldParent) domUtils.removeItem(oldParent);
620
+ if (oldParent.childNodes.length === 0 && parentNode !== oldParent) dom.utils.removeItem(oldParent);
496
621
  }
497
622
 
498
- if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !domUtils.isListCell(parentNode) && !domUtils.isWysiwygFrame(parentNode)) {
623
+ if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
499
624
  afterNode = parentNode.nextElementSibling;
500
625
  parentNode = parentNode.parentNode;
501
626
  }
502
627
 
503
- if (domUtils.isWysiwygFrame(parentNode) && (oNode.nodeType === 3 || domUtils.isBreak(oNode))) {
504
- const fNode = domUtils.createElement(this.options.get('defaultLine'), null, oNode);
505
- oNode = fNode;
628
+ if (dom.check.isWysiwygFrame(parentNode) && (oNode.nodeType === 3 || dom.check.isBreak(oNode))) {
629
+ const formatNode = dom.utils.createElement(this.options.get('defaultLine'), null, oNode);
630
+ fNode = oNode;
631
+ oNode = formatNode;
506
632
  }
507
633
  }
508
634
 
@@ -519,12 +645,12 @@ HTML.prototype = {
519
645
  afterNode = parentNode === afterNode ? parentNode.lastChild : afterNode;
520
646
  }
521
647
 
522
- if (domUtils.isListCell(oNode) && !domUtils.isList(parentNode)) {
523
- if (domUtils.isListCell(parentNode)) {
648
+ if (dom.check.isListCell(oNode) && !dom.check.isList(parentNode)) {
649
+ if (dom.check.isListCell(parentNode)) {
524
650
  afterNode = parentNode.nextElementSibling;
525
651
  parentNode = parentNode.parentNode;
526
652
  } else {
527
- const ul = domUtils.createElement('ol');
653
+ const ul = dom.utils.createElement('ol');
528
654
  parentNode.insertBefore(ul, afterNode);
529
655
  parentNode = ul;
530
656
  afterNode = null;
@@ -536,11 +662,11 @@ HTML.prototype = {
536
662
  parentNode.insertBefore(oNode, afterNode);
537
663
 
538
664
  if (insertListCell) {
539
- if (domUtils.isZeroWith(line.textContent.trim())) {
540
- domUtils.removeItem(line);
665
+ if (dom.check.isZeroWidth(line.textContent.trim())) {
666
+ dom.utils.removeItem(line);
541
667
  oNode = oNode.lastChild;
542
668
  } else {
543
- const chList = domUtils.getArrayItem(line.children, domUtils.isList);
669
+ const chList = dom.utils.arrayFind(line.children, dom.check.isList);
544
670
  if (chList) {
545
671
  if (oNode !== chList) {
546
672
  oNode.appendChild(chList);
@@ -550,8 +676,8 @@ HTML.prototype = {
550
676
  oNode = parentNode;
551
677
  }
552
678
 
553
- if (domUtils.isZeroWith(line.textContent.trim())) {
554
- domUtils.removeItem(line);
679
+ if (dom.check.isZeroWidth(line.textContent.trim())) {
680
+ dom.utils.removeItem(line);
555
681
  }
556
682
  }
557
683
  }
@@ -560,6 +686,8 @@ HTML.prototype = {
560
686
  parentNode.appendChild(oNode);
561
687
  console.warn('[SUNEDITOR.html.insertNode.warn]', error);
562
688
  } finally {
689
+ if (fNode) oNode = fNode;
690
+
563
691
  const dupleNodes = parentNode.querySelectorAll('[data-duple]');
564
692
  if (dupleNodes.length > 0) {
565
693
  for (let i = 0, len = dupleNodes.length, d, c, ch, parent; i < len; i++) {
@@ -573,14 +701,14 @@ HTML.prototype = {
573
701
  }
574
702
 
575
703
  if (d === oNode) oNode = c;
576
- domUtils.removeItem(d);
704
+ dom.utils.removeItem(d);
577
705
  }
578
706
  }
579
707
 
580
708
  if ((this.format.isLine(oNode) || this.component.isBasic(oNode)) && startCon === endCon) {
581
709
  const cItem = this.format.getLine(commonCon, null);
582
- if (cItem?.nodeType === 1 && domUtils.isEmptyLine(cItem)) {
583
- domUtils.removeItem(cItem);
710
+ if (cItem?.nodeType === 1 && dom.check.isEmptyLine(cItem)) {
711
+ dom.utils.removeItem(cItem);
584
712
  }
585
713
  }
586
714
 
@@ -591,39 +719,17 @@ HTML.prototype = {
591
719
  if (!this.component.isBasic(oNode)) {
592
720
  let offset = 1;
593
721
  if (oNode.nodeType === 3) {
594
- const previous = oNode.previousSibling;
595
- const next = oNode.nextSibling;
596
- const previousText = !previous || previous.nodeType === 1 || domUtils.isZeroWith(previous) ? '' : previous.textContent;
597
- const nextText = !next || next.nodeType === 1 || domUtils.isZeroWith(next) ? '' : next.textContent;
598
-
599
- if (previous && previousText.length > 0) {
600
- oNode.textContent = previousText + oNode.textContent;
601
- domUtils.removeItem(previous);
602
- }
603
-
604
- if (next && nextText.length > 0) {
605
- oNode.textContent += nextText;
606
- domUtils.removeItem(next);
607
- }
608
-
609
- const newRange = {
610
- container: oNode,
611
- startOffset: previousText.length,
612
- endOffset: oNode.textContent.length - nextText.length
613
- };
614
-
615
- this.selection.setRange(oNode, newRange.startOffset, oNode, newRange.endOffset);
616
-
617
- result = newRange;
618
- } else if (!domUtils.isBreak(oNode) && !domUtils.isListCell(oNode) && this.format.isLine(parentNode)) {
722
+ offset = oNode.textContent.length;
723
+ this.selection.setRange(oNode, offset, oNode, offset);
724
+ } else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.format.isLine(parentNode)) {
619
725
  let zeroWidth = null;
620
- if (!oNode.previousSibling || domUtils.isBreak(oNode.previousSibling)) {
621
- zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
726
+ if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
727
+ zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
622
728
  oNode.parentNode.insertBefore(zeroWidth, oNode);
623
729
  }
624
730
 
625
- if (!oNode.nextSibling || domUtils.isBreak(oNode.nextSibling)) {
626
- zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
731
+ if (!oNode.nextSibling || dom.check.isBreak(oNode.nextSibling)) {
732
+ zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
627
733
  oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
628
734
  }
629
735
 
@@ -636,26 +742,27 @@ HTML.prototype = {
636
742
  }
637
743
  }
638
744
 
639
- if (!result) {
640
- this.history.push(true);
641
- result = oNode;
642
- }
745
+ result = oNode;
643
746
  }
644
747
 
645
748
  return result;
646
749
  },
647
750
 
648
751
  /**
752
+ * @this {HTMLThis}
649
753
  * @description Delete the selected range.
650
- * Returns {container: "the last element after deletion", offset: "offset", prevContainer: "previousElementSibling Of the deleted area"}
651
- * @returns {Object}
754
+ * @returns {{container: Node, offset: number, commonCon?: Node|null, prevContainer?: Node|null}}
755
+ * - container: "the last element after deletion"
756
+ * - offset: "offset"
757
+ * - commonCon: "commonAncestorContainer"
758
+ * - prevContainer: "previousElementSibling Of the deleted area"
652
759
  */
653
760
  remove() {
654
761
  this.selection._resetRangeToTextNode();
655
762
 
656
763
  const range = this.selection.getRange();
657
764
  const isStartEdge = range.startOffset === 0;
658
- const isEndEdge = domUtils.isEdgePoint(range.endContainer, range.endOffset, 'end');
765
+ const isEndEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
659
766
  let prevContainer = null;
660
767
  let startPrevEl = null;
661
768
  let endNextEl = null;
@@ -675,62 +782,66 @@ HTML.prototype = {
675
782
  let endCon = range.endContainer;
676
783
  let startOff = range.startOffset;
677
784
  let endOff = range.endOffset;
678
- const commonCon = range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer;
785
+ const commonCon = /** @type {HTMLElement} */ (range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer);
679
786
  if (commonCon === startCon && commonCon === endCon) {
680
787
  if (this.component.isBasic(commonCon)) {
681
788
  const compInfo = this.component.get(commonCon);
682
789
  const compContainer = compInfo.container;
683
- const parent = compContainer.parentNode;
790
+ const parent = compContainer.parentElement;
684
791
 
685
792
  const next = compContainer.nextSibling || compContainer.previousSibling;
686
793
  const nextOffset = next === compContainer.previousSibling ? next?.textContent?.length || 1 : 0;
687
794
  const parentNext = parent.nextElementSibling || parent.previousElementSibling;
688
795
  const parentNextOffset = parentNext === parent.previousElementSibling ? parentNext?.textContent?.length || 1 : 0;
689
796
 
690
- domUtils.removeItem(compContainer);
797
+ dom.utils.removeItem(compContainer);
691
798
 
692
799
  if (this.format.isLine(parent)) {
693
800
  if (parent.childNodes.length === 0) {
694
- domUtils.removeItem(parent);
801
+ dom.utils.removeItem(parent);
695
802
  return {
696
803
  container: parentNext,
697
- offset: parentNextOffset
804
+ offset: parentNextOffset,
805
+ commonCon
698
806
  };
699
807
  } else {
700
808
  return {
701
809
  container: next,
702
- offset: nextOffset
810
+ offset: nextOffset,
811
+ commonCon
703
812
  };
704
813
  }
705
814
  } else {
706
815
  return {
707
816
  container: parentNext,
708
- offset: parentNextOffset
817
+ offset: parentNextOffset,
818
+ commonCon
709
819
  };
710
820
  }
711
821
  } else {
712
822
  if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
713
- const nextEl = domUtils.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwygFrame'));
714
- const prevEl = domUtils.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwygFrame'));
823
+ const nextEl = dom.query.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
824
+ const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
715
825
  const line = this.format.getLine(commonCon);
716
- domUtils.removeItem(commonCon);
826
+ dom.utils.removeItem(commonCon);
717
827
 
718
828
  let rEl = nextEl || prevEl;
719
829
  let rOffset = nextEl ? 0 : rEl?.nodeType === 3 ? rEl.textContent.length : 1;
720
830
 
721
831
  const npEl = this.format.getLine(rEl) || this.component.get(rEl);
722
832
  if (line !== npEl) {
723
- rEl = npEl;
833
+ rEl = /** @type {Node} */ (npEl);
724
834
  rOffset = rOffset === 0 ? 0 : 1;
725
835
  }
726
836
 
727
- if (domUtils.isZeroWith(line)) {
728
- domUtils.removeItem(line);
837
+ if (dom.check.isZeroWidth(line) && !line.contains(rEl)) {
838
+ dom.utils.removeItem(line);
729
839
  }
730
840
 
731
841
  return {
732
842
  container: rEl,
733
- offset: rOffset
843
+ offset: rOffset,
844
+ commonCon
734
845
  };
735
846
  }
736
847
 
@@ -743,15 +854,17 @@ HTML.prototype = {
743
854
  if (!startCon || !endCon)
744
855
  return {
745
856
  container: commonCon,
746
- offset: 0
857
+ offset: 0,
858
+ commonCon
747
859
  };
748
860
 
749
861
  if (startCon === endCon && range.collapsed) {
750
- if (domUtils.isZeroWith(startCon.textContent?.substr(startOff))) {
862
+ if (dom.check.isZeroWidth(startCon.textContent?.substring(startOff))) {
751
863
  return {
752
864
  container: startCon,
753
865
  offset: startOff,
754
- prevContainer: startCon && startCon.parentNode ? startCon : null
866
+ prevContainer: startCon && startCon.parentNode ? startCon : null,
867
+ commonCon
755
868
  };
756
869
  }
757
870
  }
@@ -759,9 +872,9 @@ HTML.prototype = {
759
872
  let beforeNode = null;
760
873
  let afterNode = null;
761
874
 
762
- const childNodes = domUtils.getListChildNodes(commonCon, null);
763
- let startIndex = domUtils.getArrayIndex(childNodes, startCon);
764
- let endIndex = domUtils.getArrayIndex(childNodes, endCon);
875
+ const childNodes = dom.query.getListChildNodes(commonCon, null);
876
+ let startIndex = dom.utils.getArrayIndex(childNodes, startCon);
877
+ let endIndex = dom.utils.getArrayIndex(childNodes, endCon);
765
878
 
766
879
  if (childNodes.length > 0 && startIndex > -1 && endIndex > -1) {
767
880
  for (let i = startIndex + 1, startNode = startCon; i >= 0; i--) {
@@ -780,25 +893,28 @@ HTML.prototype = {
780
893
  }
781
894
  } else {
782
895
  if (childNodes.length === 0) {
783
- if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) || domUtils.isWysiwygFrame(commonCon) || domUtils.isBreak(commonCon) || domUtils.isMedia(commonCon)) {
896
+ if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
784
897
  return {
785
898
  container: commonCon,
786
- offset: 0
899
+ offset: 0,
900
+ commonCon
787
901
  };
788
- } else if (commonCon.nodeType === 3) {
902
+ } else if (dom.check.isText(commonCon)) {
789
903
  return {
790
904
  container: commonCon,
791
- offset: endOff
905
+ offset: endOff,
906
+ commonCon
792
907
  };
793
908
  }
794
909
  childNodes.push(commonCon);
795
910
  startCon = endCon = commonCon;
796
911
  } else {
797
912
  startCon = endCon = childNodes[0];
798
- if (domUtils.isBreak(startCon) || domUtils.isZeroWith(startCon)) {
913
+ if (dom.check.isBreak(startCon) || dom.check.isZeroWidth(startCon)) {
799
914
  return {
800
- container: domUtils.isMedia(commonCon) ? commonCon : startCon,
801
- offset: 0
915
+ container: dom.check.isMedia(commonCon) ? commonCon : startCon,
916
+ offset: 0,
917
+ commonCon
802
918
  };
803
919
  }
804
920
  }
@@ -806,29 +922,33 @@ HTML.prototype = {
806
922
  startIndex = endIndex = 0;
807
923
  }
808
924
 
925
+ const _isText = dom.check.isText;
926
+ const _isElement = dom.check.isElement;
809
927
  for (let i = startIndex; i <= endIndex; i++) {
810
- const item = childNodes[i];
928
+ const item = /** @type {Text} */ (childNodes[i]);
811
929
 
812
- if (item.length === 0 || (item.nodeType === 3 && item.data === undefined)) {
930
+ if (_isText(item) && (item.data === undefined || item.length === 0)) {
813
931
  this._nodeRemoveListItem(item);
814
932
  continue;
815
933
  }
816
934
 
817
935
  if (item === startCon) {
818
- if (startCon.nodeType === 1) {
936
+ if (_isElement(startCon)) {
819
937
  if (this.component.is(startCon)) continue;
820
- else beforeNode = domUtils.createTextNode(startCon.textContent);
938
+ else beforeNode = dom.utils.createTextNode(startCon.textContent);
821
939
  } else {
940
+ const sc = /** @type {Text} */ (startCon);
941
+ const ec = /** @type {Text} */ (endCon);
822
942
  if (item === endCon) {
823
- beforeNode = domUtils.createTextNode(startCon.substringData(0, startOff) + endCon.substringData(endOff, endCon.length - endOff));
943
+ beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff) + ec.substringData(endOff, ec.length - endOff));
824
944
  offset = startOff;
825
945
  } else {
826
- beforeNode = domUtils.createTextNode(startCon.substringData(0, startOff));
946
+ beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff));
827
947
  }
828
948
  }
829
949
 
830
950
  if (beforeNode.length > 0) {
831
- startCon.data = beforeNode.data;
951
+ /** @type {Text} */ (startCon).data = beforeNode.data;
832
952
  } else {
833
953
  this._nodeRemoveListItem(startCon);
834
954
  }
@@ -838,15 +958,15 @@ HTML.prototype = {
838
958
  }
839
959
 
840
960
  if (item === endCon) {
841
- if (endCon.nodeType === 1) {
842
- if (this.component.is(endCon)) continue;
843
- else afterNode = domUtils.createTextNode(endCon.textContent);
961
+ if (_isText(endCon)) {
962
+ afterNode = dom.utils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
844
963
  } else {
845
- afterNode = domUtils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
964
+ if (this.component.is(endCon)) continue;
965
+ else afterNode = dom.utils.createTextNode(endCon.textContent);
846
966
  }
847
967
 
848
968
  if (afterNode.length > 0) {
849
- endCon.data = afterNode.data;
969
+ /** @type {Text} */ (endCon).data = afterNode.data;
850
970
  } else {
851
971
  this._nodeRemoveListItem(endCon);
852
972
  }
@@ -857,14 +977,25 @@ HTML.prototype = {
857
977
  this._nodeRemoveListItem(item);
858
978
  }
859
979
 
860
- const endUl = domUtils.getParentElement(endCon, 'ul');
861
- const startLi = domUtils.getParentElement(startCon, 'li');
980
+ const endUl = dom.query.getParentElement(endCon, 'ul');
981
+ const startLi = dom.query.getParentElement(startCon, 'li');
862
982
  if (endUl && startLi && startLi.contains(endUl)) {
863
983
  container = endUl.previousSibling;
864
984
  offset = container.textContent.length;
865
985
  } else {
866
986
  container = endCon && endCon.parentNode ? endCon : startCon && startCon.parentNode ? startCon : range.endContainer || range.startContainer;
867
- offset = !isStartEdge && !isEndEdge ? offset : isEndEdge ? container.textContent.length : 0;
987
+ if (isStartEdge || isEndEdge) {
988
+ if (isEndEdge) {
989
+ if (container.nodeType === 1 && container.childNodes.length === 0) {
990
+ container.appendChild(dom.utils.createElement('BR'));
991
+ offset = 1;
992
+ } else {
993
+ offset = container.textContent.length;
994
+ }
995
+ } else {
996
+ offset = 0;
997
+ }
998
+ }
868
999
  }
869
1000
 
870
1001
  if (!this.format.getLine(container) && !(startCon && startCon.parentNode)) {
@@ -877,7 +1008,7 @@ HTML.prototype = {
877
1008
  }
878
1009
  }
879
1010
 
880
- if (!domUtils.isWysiwygFrame(container) && container.childNodes.length === 0) {
1011
+ if (!dom.check.isWysiwygFrame(container) && container.childNodes.length === 0) {
881
1012
  const rc = this.nodeTransform.removeAllParents(container, null, null);
882
1013
  if (rc) container = rc.sc || rc.ec || this.editor.frameContext.get('wysiwyg');
883
1014
  }
@@ -886,21 +1017,24 @@ HTML.prototype = {
886
1017
  this.selection.setRange(container, offset, container, offset);
887
1018
 
888
1019
  return {
889
- container: container,
890
- offset: offset,
891
- prevContainer: prevContainer
1020
+ container,
1021
+ offset,
1022
+ prevContainer,
1023
+ commonCon
892
1024
  };
893
1025
  },
894
1026
 
895
1027
  /**
1028
+ * @this {HTMLThis}
896
1029
  * @description Gets the current content
897
- * @param {boolean} withFrame Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
1030
+ * @param {Object} [options] Options
1031
+ * @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
898
1032
  * Ignored for targetOptions.get('iframe_fullPage') is true.
899
- * @param {boolean} includeFullPage Return only the content of the body without headers when the "iframe_fullPage" option is true
900
- * @param {number|Array.<number>|undefined} rootKey Root index
901
- * @returns {string|Array.<string>}
1033
+ * @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the "iframe_fullPage" option is true
1034
+ * @param {number|Array<number>} [options.rootKey=null] Root index
1035
+ * @returns {string|Object<*, string>}
902
1036
  */
903
- get(withFrame, includeFullPage, rootKey) {
1037
+ get({ withFrame, includeFullPage, rootKey } = {}) {
904
1038
  if (!rootKey) rootKey = [this.status.rootKey];
905
1039
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
906
1040
 
@@ -910,23 +1044,23 @@ HTML.prototype = {
910
1044
  this.editor.changeFrameContext(rootKey[i]);
911
1045
 
912
1046
  const fc = this.editor.frameContext;
913
- const renderHTML = domUtils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
914
- const editableEls = domUtils.getListChildren(renderHTML, (current) => current.hasAttribute('contenteditable'));
1047
+ const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
1048
+ const editableEls = dom.query.getListChildren(renderHTML, (current) => current.hasAttribute('contenteditable'));
915
1049
 
916
1050
  for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
917
1051
  editableEls[j].removeAttribute('contenteditable');
918
1052
  }
919
1053
 
920
- const content = this.clean(renderHTML.innerHTML, false, null, null);
1054
+ const content = renderHTML.innerHTML;
921
1055
  if (this.editor.frameOptions.get('iframe_fullPage')) {
922
1056
  if (includeFullPage) {
923
- const attrs = domUtils.getAttributesToString(fc.get('_wd').body, ['contenteditable']);
924
- r = '<!DOCTYPE html><html>' + fc.get('_wd').head.outerHTML + '<body ' + attrs + '>' + content + '</body></html>';
1057
+ const attrs = dom.utils.getAttributesToString(fc.get('_wd').body, ['contenteditable']);
1058
+ r = `<!DOCTYPE html><html>${fc.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
925
1059
  } else {
926
1060
  r = content;
927
1061
  }
928
1062
  } else {
929
- r = withFrame ? '<div class="sun-editor-editable' + (this.options.get('_rtl') ? ' se-rtl' : '') + '">' + content + '</div>' : renderHTML.innerHTML;
1063
+ r = withFrame ? `<div class="${this.options.get('_editableClass') + '' + (this.options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
930
1064
  }
931
1065
 
932
1066
  resultValue[rootKey[i]] = r;
@@ -937,13 +1071,15 @@ HTML.prototype = {
937
1071
  },
938
1072
 
939
1073
  /**
1074
+ * @this {HTMLThis}
940
1075
  * @description Sets the HTML string
941
- * @param {string|undefined} html HTML string
942
- * @param {number|Array.<number>|undefined} rootKey Root index
1076
+ * @param {string} html HTML string
1077
+ * @param {Object} [options] Options
1078
+ * @param {number|Array<number>} [options.rootKey=null] Root index
943
1079
  */
944
- set(html, rootKey) {
1080
+ set(html, { rootKey } = {}) {
945
1081
  this.selection.removeRange();
946
- const convertValue = html === null || html === undefined ? '' : this.clean(html, true, null, null);
1082
+ const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
947
1083
 
948
1084
  if (!rootKey) rootKey = [this.status.rootKey];
949
1085
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
@@ -963,20 +1099,22 @@ HTML.prototype = {
963
1099
  },
964
1100
 
965
1101
  /**
1102
+ * @this {HTMLThis}
966
1103
  * @description Add content to the end of content.
967
- * @param {string} content Content to Input
968
- * @param {number|Array.<number>|undefined} rootKey Root index
1104
+ * @param {string} html Content to Input
1105
+ * @param {Object} [options] Options
1106
+ * @param {number|Array<number>} [options.rootKey=null] Root index
969
1107
  */
970
- add(content, rootKey) {
1108
+ add(html, { rootKey } = {}) {
971
1109
  if (!rootKey) rootKey = [this.status.rootKey];
972
1110
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
973
1111
 
974
1112
  for (let i = 0; i < rootKey.length; i++) {
975
1113
  this.editor.changeFrameContext(rootKey[i]);
976
- const convertValue = this.clean(content, true, null, null);
1114
+ const convertValue = this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
977
1115
 
978
1116
  if (!this.editor.frameContext.get('isCodeView')) {
979
- const temp = domUtils.createElement('DIV', null, convertValue);
1117
+ const temp = dom.utils.createElement('DIV', null, convertValue);
980
1118
  const children = temp.children;
981
1119
  const len = children.length;
982
1120
  for (let j = 0; j < len; j++) {
@@ -992,11 +1130,13 @@ HTML.prototype = {
992
1130
  },
993
1131
 
994
1132
  /**
1133
+ * @this {HTMLThis}
995
1134
  * @description Sets the content of the iframe's head tag and body tag when using the "iframe" or "iframe_fullPage" option.
996
- * @param {Object} ctx { head: HTML string, body: HTML string}
997
- * @param {number|Array.<number>|undefined} rootKey Root index
1135
+ * @param {{head: string, body: string}} ctx { head: HTML string, body: HTML string}
1136
+ * @param {Object} [options] Options
1137
+ * @param {number|Array<number>} [options.rootKey=null] Root index
998
1138
  */
999
- setFullPage(ctx, rootKey) {
1139
+ setFullPage(ctx, { rootKey } = {}) {
1000
1140
  if (!this.editor.frameOptions.get('iframe')) return false;
1001
1141
 
1002
1142
  if (!rootKey) rootKey = [this.status.rootKey];
@@ -1005,24 +1145,30 @@ HTML.prototype = {
1005
1145
  for (let i = 0; i < rootKey.length; i++) {
1006
1146
  this.editor.changeFrameContext(rootKey[i]);
1007
1147
  if (ctx.head) this.editor.frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.__disallowedTagsRegExp, '');
1008
- if (ctx.body) this.editor.frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, true, null, null);
1148
+ if (ctx.body) this.editor.frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
1009
1149
  this.editor._resetComponents();
1010
1150
  }
1011
1151
  },
1012
1152
 
1013
1153
  /**
1154
+ * @this {HTMLThis}
1014
1155
  * @description HTML code compression
1015
1156
  * @param {string} html HTML string
1016
1157
  * @returns {string} HTML string
1017
1158
  */
1018
1159
  compress(html) {
1019
- return html.replace(/\n/g, '').replace(/(>)(?:\s+)(<)/g, '$1$2');
1160
+ return html
1161
+ .replace(/\n/g, '')
1162
+ .replace(/(>)(?:\s+)(<)/g, '$1$2')
1163
+ .trim();
1020
1164
  },
1021
1165
 
1022
1166
  /**
1167
+ * @private
1168
+ * @this {HTMLThis}
1023
1169
  * @description construct wysiwyg area element to html string
1024
- * @param {Element|String} html WYSIWYG element (this.editor.frameContext.get('wysiwyg')) or HTML string.
1025
- * @param {Boolean} comp If true, does not line break and indentation of tags.
1170
+ * @param {Node|string} html WYSIWYG element (this.editor.frameContext.get('wysiwyg')) or HTML string.
1171
+ * @param {boolean} comp If true, does not line break and indentation of tags.
1026
1172
  * @returns {string}
1027
1173
  */
1028
1174
  _convertToCode(html, comp) {
@@ -1035,8 +1181,8 @@ HTML.prototype = {
1035
1181
  };
1036
1182
  const brChar = comp ? '' : '\n';
1037
1183
 
1038
- let indentSize = comp ? 0 : this.status.codeIndentSize * 1;
1039
- indentSize = indentSize > 0 ? new Array(indentSize + 1).join(' ') : '';
1184
+ const codeSize = comp ? 0 : this.status.codeIndentSize * 1;
1185
+ const indentSize = codeSize > 0 ? new Array(codeSize + 1).join(' ') : '';
1040
1186
 
1041
1187
  (function recursionFunc(element, indent) {
1042
1188
  const children = element.childNodes;
@@ -1054,22 +1200,22 @@ HTML.prototype = {
1054
1200
  continue;
1055
1201
  }
1056
1202
  if (node.nodeType === 3) {
1057
- if (!domUtils.isList(node.parentElement)) returnHTML += converter.htmlToEntity(/^\n+$/.test(node.data) ? '' : node.data);
1203
+ if (!dom.check.isList(node.parentElement)) returnHTML += converter.htmlToEntity(/^\n+$/.test(/** @type {Text} */ (node).data) ? '' : /** @type {Text} */ (node).data);
1058
1204
  continue;
1059
1205
  }
1060
1206
  if (node.childNodes.length === 0) {
1061
- returnHTML += (/^HR$/i.test(node.nodeName) ? brChar : '') + (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName) ? '' : elementIndent) + node.outerHTML + br;
1207
+ returnHTML += (/^HR$/i.test(node.nodeName) ? brChar : '') + (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName) ? '' : elementIndent) + /** @type {HTMLElement} */ (node).outerHTML + br;
1062
1208
  continue;
1063
1209
  }
1064
1210
 
1065
- if (!node.outerHTML) {
1211
+ if (!(/** @type {HTMLElement} */ (node).outerHTML)) {
1066
1212
  returnHTML += new XMLSerializer().serializeToString(node);
1067
1213
  } else {
1068
1214
  tag = node.nodeName.toLowerCase();
1069
1215
  tagIndent = elementIndent || nodeRegTest ? indent : '';
1070
- returnHTML += (lineBR || (elementRegTest ? '' : br)) + tagIndent + node.outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] + br;
1071
- recursionFunc(node, indent + indentSize, '');
1072
- returnHTML += (/\n$/.test(returnHTML) ? tagIndent : '') + '</' + tag + '>' + (lineBR || br || elementRegTest ? brChar : '' || /^(TH|TD)$/i.test(node.nodeName) ? brChar : '');
1216
+ returnHTML += (lineBR || (elementRegTest ? '' : br)) + tagIndent + /** @type {HTMLElement} */ (node).outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] + br;
1217
+ recursionFunc(node, indent + indentSize + '');
1218
+ returnHTML += (/\n$/.test(returnHTML) ? tagIndent : '') + '</' + tag + '>' + (lineBR || br || elementRegTest ? brChar : /^(TH|TD)$/i.test(node.nodeName) ? brChar : '');
1073
1219
  }
1074
1220
  }
1075
1221
  })(wDoc, '');
@@ -1077,24 +1223,31 @@ HTML.prototype = {
1077
1223
  return returnHTML.trim() + brChar;
1078
1224
  },
1079
1225
 
1226
+ /**
1227
+ * @private
1228
+ * @this {HTMLThis}
1229
+ * @description Checks whether the given list item node should be removed and handles necessary clean-up.
1230
+ * @param {Node} item The list item node to be checked.
1231
+ */
1080
1232
  _nodeRemoveListItem(item) {
1081
1233
  const line = this.format.getLine(item, null);
1082
- domUtils.removeItem(item);
1234
+ dom.utils.removeItem(item);
1083
1235
 
1084
- if (!domUtils.isListCell(line)) return;
1236
+ if (!dom.check.isListCell(line)) return;
1085
1237
 
1086
1238
  this.nodeTransform.removeAllParents(line, null, null);
1087
1239
 
1088
- if (domUtils.isList(line?.firstChild)) {
1089
- line.insertBefore(domUtils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
1240
+ if (dom.check.isList(line?.firstChild)) {
1241
+ line.insertBefore(dom.utils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
1090
1242
  }
1091
1243
  },
1092
1244
 
1093
1245
  /**
1246
+ * @private
1247
+ * @this {HTMLThis}
1094
1248
  * @description Recursive function when used to place a node in "BrLine" in "html.insertNode"
1095
1249
  * @param {Node} oNode Node to be inserted
1096
1250
  * @returns {Node} "oNode"
1097
- * @private
1098
1251
  */
1099
1252
  _setIntoFreeFormat(oNode) {
1100
1253
  const parentNode = oNode.parentNode;
@@ -1116,8 +1269,8 @@ HTML.prototype = {
1116
1269
  parentNode.insertBefore(lastONode, oNode);
1117
1270
  }
1118
1271
 
1119
- if (oNode.childNodes.length === 0) domUtils.removeItem(oNode);
1120
- oNode = domUtils.createElement('BR');
1272
+ if (oNode.childNodes.length === 0) dom.utils.removeItem(oNode);
1273
+ oNode = dom.utils.createElement('BR');
1121
1274
  parentNode.insertBefore(oNode, lastONode.nextSibling);
1122
1275
  }
1123
1276
 
@@ -1125,43 +1278,47 @@ HTML.prototype = {
1125
1278
  },
1126
1279
 
1127
1280
  /**
1281
+ * @private
1282
+ * @this {HTMLThis}
1128
1283
  * @description Returns HTML string according to tag type and configurati isExcludeFormat.
1129
1284
  * @param {Node} node Node
1130
- * @param {boolean} requireFormat If true, text nodes that do not have a format node is wrapped with the format tag.
1131
- * @private
1285
+ * @param {boolean} forceFormat If true, text nodes that do not have a format node is wrapped with the format tag.
1132
1286
  */
1133
- _makeLine(node, requireFormat) {
1287
+ _makeLine(node, forceFormat) {
1134
1288
  const defaultLine = this.options.get('defaultLine');
1135
1289
  // element
1136
1290
  if (node.nodeType === 1) {
1137
1291
  if (this.__disallowedTagNameRegExp.test(node.nodeName)) return '';
1138
- if (domUtils.isExcludeFormat(node)) return node.outerHTML;
1292
+ if (dom.check.isExcludeFormat(node)) return node.outerHTML;
1139
1293
 
1140
1294
  const ch =
1141
- domUtils.getListChildNodes(node, (current) => {
1142
- return domUtils.isSpanWithoutAttr(current) && !domUtils.getParentElement(current, domUtils.isExcludeFormat);
1295
+ dom.query.getListChildNodes(node, (current) => {
1296
+ return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1143
1297
  }) || [];
1144
- for (let i = ch.length - 1; i >= 0; i--) {
1145
- ch[i].outerHTML = ch[i].innerHTML;
1298
+ for (let i = ch.length - 1, c; i >= 0; i--) {
1299
+ c = /** @type {HTMLElement} */ (ch[i]);
1300
+ c.outerHTML = c.innerHTML;
1146
1301
  }
1147
1302
 
1148
1303
  if (
1149
- !requireFormat ||
1304
+ !forceFormat ||
1150
1305
  this.format.isLine(node) ||
1151
1306
  this.format.isBlock(node) ||
1152
1307
  this.component.is(node) ||
1153
- domUtils.isMedia(node) ||
1154
- domUtils.isFigure(node) ||
1155
- (domUtils.isAnchor(node) && domUtils.isMedia(node.firstElementChild))
1308
+ dom.check.isMedia(node) ||
1309
+ dom.check.isFigure(node) ||
1310
+ (dom.check.isAnchor(node) && dom.check.isMedia(node.firstElementChild))
1156
1311
  ) {
1157
- return domUtils.isSpanWithoutAttr(node) ? node.innerHTML : node.outerHTML;
1312
+ const n = /** @type {HTMLElement} */ (node);
1313
+ return dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML;
1158
1314
  } else {
1159
- return '<' + defaultLine + '>' + (domUtils.isSpanWithoutAttr(node) ? node.innerHTML : node.outerHTML) + '</' + defaultLine + '>';
1315
+ const n = /** @type {HTMLElement} */ (node);
1316
+ return '<' + defaultLine + '>' + (dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML) + '</' + defaultLine + '>';
1160
1317
  }
1161
1318
  }
1162
1319
  // text
1163
1320
  if (node.nodeType === 3) {
1164
- if (!requireFormat) return converter.htmlToEntity(node.textContent);
1321
+ if (!forceFormat) return converter.htmlToEntity(node.textContent);
1165
1322
  const textArray = node.textContent.split(/\n/g);
1166
1323
  let html = '';
1167
1324
  for (let i = 0, tLen = textArray.length, text; i < tLen; i++) {
@@ -1179,43 +1336,48 @@ HTML.prototype = {
1179
1336
  },
1180
1337
 
1181
1338
  /**
1339
+ * @private
1340
+ * @this {HTMLThis}
1182
1341
  * @description Fix tags that do not fit the editor format.
1183
- * @param {Element} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
1342
+ * @param {DocumentFragment} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
1184
1343
  * @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist
1185
1344
  * @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist
1186
- * @private
1345
+ * @param {boolean} tagFilter Tag filter option
1346
+ * @param {boolean} formatFilter Format filter option
1347
+ * @param {boolean} classFilter Class name filter option
1348
+ * @param {boolean} _freeCodeViewMode Enforces strict HTML validation based on the editor`s policy
1187
1349
  */
1188
- _consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter) {
1350
+ _consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
1189
1351
  const removeTags = [],
1190
1352
  emptyTags = [],
1191
1353
  wrongList = [],
1192
1354
  withoutFormatCells = [];
1193
1355
 
1194
1356
  // wrong position
1195
- const wrongTags = domUtils.getListChildNodes(documentFragment, (current) => {
1357
+ const wrongTags = dom.query.getListChildNodes(documentFragment, (current) => {
1196
1358
  if (formatFilter && current.nodeType !== 1) {
1197
- if (domUtils.isList(current.parentElement)) removeTags.push(current);
1359
+ if (dom.check.isList(current.parentElement)) removeTags.push(current);
1198
1360
  return false;
1199
1361
  }
1200
1362
 
1201
1363
  // tag filter
1202
1364
  if (tagFilter) {
1203
1365
  // white list
1204
- if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && domUtils.isExcludeFormat(current))) {
1366
+ if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
1205
1367
  removeTags.push(current);
1206
1368
  return false;
1207
1369
  }
1208
1370
  }
1209
1371
 
1210
- const nrtag = !domUtils.getParentElement(current, domUtils.isExcludeFormat);
1372
+ const nrtag = !dom.query.getParentElement(current, dom.check.isExcludeFormat);
1211
1373
 
1212
1374
  // formatFilter
1213
1375
  if (formatFilter) {
1214
1376
  // empty tags
1215
1377
  if (
1216
- !domUtils.isTableElements(current) &&
1217
- !domUtils.isListCell(current) &&
1218
- !domUtils.isAnchor(current) &&
1378
+ !dom.check.isTableElements(current) &&
1379
+ !dom.check.isListCell(current) &&
1380
+ !dom.check.isAnchor(current) &&
1219
1381
  (this.format.isLine(current) || this.format.isBlock(current) || this.format.isTextStyleNode(current)) &&
1220
1382
  current.childNodes.length === 0 &&
1221
1383
  nrtag
@@ -1225,13 +1387,13 @@ HTML.prototype = {
1225
1387
  }
1226
1388
 
1227
1389
  // wrong list
1228
- if (domUtils.isList(current.parentNode) && !domUtils.isList(current) && !domUtils.isListCell(current)) {
1390
+ if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
1229
1391
  wrongList.push(current);
1230
1392
  return false;
1231
1393
  }
1232
1394
 
1233
1395
  // table cells
1234
- if (domUtils.isTableCell(current)) {
1396
+ if (dom.check.isTableCell(current)) {
1235
1397
  const fel = current.firstElementChild;
1236
1398
  if (!this.format.isLine(fel) && !this.format.isBlock(fel) && !this.component.is(fel)) {
1237
1399
  withoutFormatCells.push(current);
@@ -1255,16 +1417,17 @@ HTML.prototype = {
1255
1417
  }
1256
1418
 
1257
1419
  const result =
1420
+ !_freeCodeViewMode &&
1258
1421
  current.parentNode !== documentFragment &&
1259
1422
  nrtag &&
1260
- ((domUtils.isListCell(current) && !domUtils.isList(current.parentNode)) ||
1261
- ((this.format.isLine(current) || this.component.is(current)) && !this.format.isBlock(current.parentNode) && !domUtils.getParentElement(current, this.component.is.bind(this.component))));
1423
+ ((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
1424
+ ((this.format.isLine(current) || this.component.is(current)) && !this.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.component.is.bind(this.component))));
1262
1425
 
1263
1426
  return result;
1264
1427
  });
1265
1428
 
1266
1429
  for (let i = 0, len = removeTags.length; i < len; i++) {
1267
- domUtils.removeItem(removeTags[i]);
1430
+ dom.utils.removeItem(removeTags[i]);
1268
1431
  }
1269
1432
 
1270
1433
  const checkTags = [];
@@ -1273,7 +1436,7 @@ HTML.prototype = {
1273
1436
  p = t.parentNode;
1274
1437
  if (!p || !p.parentNode) continue;
1275
1438
 
1276
- if (domUtils.getParentElement(t, domUtils.isListCell)) {
1439
+ if (dom.query.getParentElement(t, dom.check.isListCell)) {
1277
1440
  const cellChildren = t.childNodes;
1278
1441
  for (let j = cellChildren.length - 1; len >= 0; j--) {
1279
1442
  p.insertBefore(t, cellChildren[j]);
@@ -1287,13 +1450,13 @@ HTML.prototype = {
1287
1450
 
1288
1451
  for (let i = 0, len = checkTags.length, t; i < len; i++) {
1289
1452
  t = checkTags[i];
1290
- if (domUtils.isZeroWith(t.textContent.trim())) {
1291
- domUtils.removeItem(t);
1453
+ if (dom.check.isZeroWidth(t.textContent.trim())) {
1454
+ dom.utils.removeItem(t);
1292
1455
  }
1293
1456
  }
1294
1457
 
1295
1458
  for (let i = 0, len = emptyTags.length; i < len; i++) {
1296
- domUtils.removeItem(emptyTags[i]);
1459
+ dom.utils.removeItem(emptyTags[i]);
1297
1460
  }
1298
1461
 
1299
1462
  for (let i = 0, len = wrongList.length, t, tp, children, p; i < len; i++) {
@@ -1301,7 +1464,7 @@ HTML.prototype = {
1301
1464
  p = t.parentNode;
1302
1465
  if (!p) continue;
1303
1466
 
1304
- tp = domUtils.createElement('LI');
1467
+ tp = dom.utils.createElement('LI');
1305
1468
 
1306
1469
  if (this.format.isLine(t)) {
1307
1470
  children = t.childNodes;
@@ -1309,7 +1472,7 @@ HTML.prototype = {
1309
1472
  tp.appendChild(children[0]);
1310
1473
  }
1311
1474
  p.insertBefore(tp, t);
1312
- domUtils.removeItem(t);
1475
+ dom.utils.removeItem(t);
1313
1476
  } else {
1314
1477
  t = t.nextSibling;
1315
1478
  tp.appendChild(wrongList[i]);
@@ -1319,17 +1482,18 @@ HTML.prototype = {
1319
1482
 
1320
1483
  for (let i = 0, len = withoutFormatCells.length, t, f; i < len; i++) {
1321
1484
  t = withoutFormatCells[i];
1322
- f = domUtils.createElement('DIV');
1485
+ f = dom.utils.createElement('DIV');
1323
1486
  f.innerHTML = t.textContent.trim().length === 0 && t.children.length === 0 ? '<br>' : t.innerHTML;
1324
1487
  t.innerHTML = f.outerHTML;
1325
1488
  }
1326
1489
  },
1327
1490
 
1328
1491
  /**
1492
+ * @private
1493
+ * @this {HTMLThis}
1329
1494
  * @description Removes attribute values such as style and converts tags that do not conform to the "html5" standard.
1330
1495
  * @param {string} html HTML string
1331
1496
  * @returns {string} HTML string
1332
- * @private
1333
1497
  */
1334
1498
  _styleNodeConvertor(html) {
1335
1499
  if (!this._disallowedStyleNodesRegExp) return html;
@@ -1341,18 +1505,19 @@ HTML.prototype = {
1341
1505
  },
1342
1506
 
1343
1507
  /**
1344
- * @description Determines if formatting is required and returns a domTree
1345
- * @param {Element} dom documentFragment
1346
- * @returns {Element}
1347
1508
  * @private
1509
+ * @this {HTMLThis}
1510
+ * @description Determines if formatting is required and returns a domTree
1511
+ * @param {DocumentFragment} domFrag documentFragment
1512
+ * @returns {DocumentFragment}
1348
1513
  */
1349
- _editFormat(dom) {
1514
+ _editFormat(domFrag) {
1350
1515
  let value = '',
1351
1516
  f;
1352
- const tempTree = dom.childNodes;
1517
+ const tempTree = domFrag.childNodes;
1353
1518
 
1354
1519
  for (let i = 0, len = tempTree.length, n; i < len; i++) {
1355
- n = tempTree[i];
1520
+ n = /** @type {HTMLElement} */ (tempTree[i]);
1356
1521
  if (this.__allowedTagNameRegExp.test(n.nodeName)) {
1357
1522
  value += n.outerHTML;
1358
1523
  continue;
@@ -1360,8 +1525,8 @@ HTML.prototype = {
1360
1525
 
1361
1526
  if (n.nodeType === 8) {
1362
1527
  value += '<!-- ' + n.textContent + ' -->';
1363
- } else if (!this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) && !/meta/i.test(n.nodeName) && !domUtils.isExcludeFormat(n)) {
1364
- if (!f) f = domUtils.createElement(this.options.get('defaultLine'));
1528
+ } else if (!/meta/i.test(n.nodeName) && !this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) && !dom.check.isExcludeFormat(n)) {
1529
+ if (!f) f = dom.utils.createElement(this.options.get('defaultLine'));
1365
1530
  f.appendChild(n);
1366
1531
  i--;
1367
1532
  len--;
@@ -1379,22 +1544,31 @@ HTML.prototype = {
1379
1544
  return this._d.createRange().createContextualFragment(value);
1380
1545
  },
1381
1546
 
1547
+ /**
1548
+ * @private
1549
+ * @this {HTMLThis}
1550
+ * @description Converts a list of DOM nodes into an HTML list structure.
1551
+ * - If the node is already a list, its innerHTML is used. If it is a block element,
1552
+ * - the function is called recursively.
1553
+ * @param {__se__NodeCollection} domTree List of DOM nodes to be converted.
1554
+ * @returns {string} The generated HTML list.
1555
+ */
1382
1556
  _convertListCell(domTree) {
1383
1557
  let html = '';
1384
1558
 
1385
1559
  for (let i = 0, len = domTree.length, node; i < len; i++) {
1386
1560
  node = domTree[i];
1387
1561
  if (node.nodeType === 1) {
1388
- if (domUtils.isList(node)) {
1562
+ if (dom.check.isList(node)) {
1389
1563
  html += node.innerHTML;
1390
- } else if (domUtils.isListCell(node)) {
1564
+ } else if (dom.check.isListCell(node)) {
1391
1565
  html += node.outerHTML;
1392
1566
  } else if (this.format.isLine(node)) {
1393
1567
  html += '<li>' + (node.innerHTML.trim() || '<br>') + '</li>';
1394
- } else if (this.format.isBlock(node) && !domUtils.isTableElements(node)) {
1395
- html += this._convertListCell(node);
1568
+ } else if (this.format.isBlock(node) && !dom.check.isTableElements(node)) {
1569
+ html += this._convertListCell(node.children);
1396
1570
  } else {
1397
- html += '<li>' + node.outerHTML + '</li>';
1571
+ html += '<li>' + /** @type {HTMLElement} */ (node).outerHTML + '</li>';
1398
1572
  }
1399
1573
  } else {
1400
1574
  html += '<li>' + (node.textContent || '<br>') + '</li>';
@@ -1404,12 +1578,19 @@ HTML.prototype = {
1404
1578
  return html;
1405
1579
  },
1406
1580
 
1581
+ /**
1582
+ * @private
1583
+ * @this {HTMLThis}
1584
+ * @description Checks whether the provided DOM nodes require formatting.
1585
+ * @param {NodeList} domTree List of DOM nodes to check.
1586
+ * @returns {boolean} True if formatting is required, otherwise false.
1587
+ */
1407
1588
  _isFormatData(domTree) {
1408
1589
  let requireFormat = false;
1409
1590
 
1410
1591
  for (let i = 0, len = domTree.length, t; i < len; i++) {
1411
1592
  t = domTree[i];
1412
- if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !domUtils.isBreak(t) && !this.__disallowedTagNameRegExp.test(t.nodeName)) {
1593
+ if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.__disallowedTagNameRegExp.test(t.nodeName)) {
1413
1594
  requireFormat = true;
1414
1595
  break;
1415
1596
  }
@@ -1418,6 +1599,16 @@ HTML.prototype = {
1418
1599
  return requireFormat;
1419
1600
  },
1420
1601
 
1602
+ /**
1603
+ * @private
1604
+ * @this {HTMLThis}
1605
+ * @description Cleans the inline style attributes of an HTML element.
1606
+ * - Extracts allowed styles and removes disallowed ones based on editor settings.
1607
+ * @param {string} m The full matched string from a regular expression.
1608
+ * @param {Array|null} v The list of allowed attributes.
1609
+ * @param {string} name The tag name of the element being cleaned.
1610
+ * @returns {Array} The updated list of allowed attributes including cleaned styles.
1611
+ */
1421
1612
  _cleanStyle(m, v, name) {
1422
1613
  let sv = (m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/) || [])[0];
1423
1614
  if (this._textStyleTags.includes(name) && !sv && (m.match(/<[^\s]+\s(.+)/) || [])[1]) {
@@ -1425,7 +1616,7 @@ HTML.prototype = {
1425
1616
  const face = (m.match(/\sface="([^"]+)"/i) || [])[1];
1426
1617
  const color = (m.match(/\scolor="([^"]+)"/i) || [])[1];
1427
1618
  if (size || face || color) {
1428
- sv = 'style="' + (size ? 'font-size:' + numbers.get(size / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
1619
+ sv = 'style="' + (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
1429
1620
  }
1430
1621
  }
1431
1622
 
@@ -1449,7 +1640,7 @@ HTML.prototype = {
1449
1640
  r = style[i].match(/([a-zA-Z0-9-]+)(:)([^"']+)/);
1450
1641
  if (r && !/inherit|initial|revert|unset/i.test(r[3])) {
1451
1642
  const k = env.kebabToCamelCase(r[1].trim());
1452
- const cs = this.editor.frameContext.get('wwComputedStyle')[k].replace(/"/g, '');
1643
+ const cs = this.editor.frameContext.get('wwComputedStyle')[k]?.replace(/"/g, '');
1453
1644
  const c = r[3].trim();
1454
1645
  switch (k) {
1455
1646
  case 'fontFamily':
@@ -1481,13 +1672,13 @@ HTML.prototype = {
1481
1672
  },
1482
1673
 
1483
1674
  /**
1675
+ * @private
1676
+ * @this {HTMLThis}
1484
1677
  * @description Delete disallowed tags
1485
1678
  * @param {string} html HTML string
1486
1679
  * @returns {string}
1487
- * @private
1488
1680
  */
1489
1681
  _deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
1490
- html = html.replace(this.__disallowedTagsRegExp, '');
1491
1682
  if (whitelistRegExp.test('<font>')) {
1492
1683
  html = html.replace(/(<\/?)font(\s?)/gi, '$1span$2');
1493
1684
  }
@@ -1495,6 +1686,13 @@ HTML.prototype = {
1495
1686
  return html.replace(whitelistRegExp, '').replace(blacklistRegExp, '');
1496
1687
  },
1497
1688
 
1689
+ /**
1690
+ * @private
1691
+ * @this {HTMLThis}
1692
+ * @description Recursively checks for duplicate text style nodes within a given parent node.
1693
+ * @param {Node} oNode The node to check for duplicate styles.
1694
+ * @param {Node} parentNode The parent node where the duplicate check occurs.
1695
+ */
1498
1696
  _checkDuplicateNode(oNode, parentNode) {
1499
1697
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1500
1698
  const inst = this;
@@ -1507,6 +1705,15 @@ HTML.prototype = {
1507
1705
  })(oNode);
1508
1706
  },
1509
1707
 
1708
+ /**
1709
+ * @private
1710
+ * @this {HTMLThis}
1711
+ * @description Recursively checks for duplicate text style nodes within a given parent node.
1712
+ * - If duplicate styles are found, redundant attributes are removed.
1713
+ * @param {Node} oNode The node to check for duplicate styles.
1714
+ * @param {Node} parentNode The parent node where the duplicate check occurs.
1715
+ * @returns {Node} The cleaned node with redundant styles removed.
1716
+ */
1510
1717
  _dupleCheck(oNode, parentNode) {
1511
1718
  if (!this.format.isTextStyleNode(oNode)) return;
1512
1719
 
@@ -1519,7 +1726,7 @@ HTML.prototype = {
1519
1726
  const inst = this.format;
1520
1727
  let duple = false;
1521
1728
  (function recursionFunc(ancestor) {
1522
- if (domUtils.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
1729
+ if (dom.check.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
1523
1730
  if (ancestor.nodeName === nodeName) {
1524
1731
  duple = true;
1525
1732
  const styles = ancestor.style.cssText.match(/[^;]+;/g) || [];
@@ -1553,11 +1760,12 @@ HTML.prototype = {
1553
1760
  };
1554
1761
 
1555
1762
  /**
1763
+ * @private
1764
+ * @this {HTMLThis}
1556
1765
  * @description Tag and tag attribute check RegExp function.
1557
1766
  * @param {string} m RegExp value
1558
1767
  * @param {string} t RegExp value
1559
1768
  * @returns {string}
1560
- * @private
1561
1769
  */
1562
1770
  function CleanElements(attrFilter, styleFilter, m, t) {
1563
1771
  if (/^<[a-z0-9]+:[a-z0-9]+/i.test(m)) return m;
@@ -1598,7 +1806,7 @@ function CleanElements(attrFilter, styleFilter, m, t) {
1598
1806
  }
1599
1807
 
1600
1808
  // figure
1601
- if (domUtils.isMedia(tagName) || domUtils.isFigure(tagName)) {
1809
+ if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
1602
1810
  const sv = m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/);
1603
1811
  if (!v) v = [];
1604
1812
  if (sv) v.push(sv[0]);
@@ -1614,6 +1822,12 @@ function CleanElements(attrFilter, styleFilter, m, t) {
1614
1822
  return t;
1615
1823
  }
1616
1824
 
1825
+ /**
1826
+ * @private
1827
+ * @description Get related list
1828
+ * @param {string} str Regular expression string
1829
+ * @param {string} str2 Regular expression string
1830
+ */
1617
1831
  function GetRegList(str, str2) {
1618
1832
  return !str ? '^' : str === '*' ? '[a-z-]+' : !str2 ? str : str + '|' + str2;
1619
1833
  }