suneditor 2.46.1 → 3.0.0-alpha.1

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 (290) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.json +64 -0
  3. package/CONTRIBUTING.md +36 -0
  4. package/LICENSE.txt +1 -1
  5. package/README.md +174 -805
  6. package/dist/suneditor.min.css +1 -0
  7. package/dist/suneditor.min.js +1 -2
  8. package/package.json +96 -70
  9. package/src/assets/icons/_default.js +194 -0
  10. package/src/assets/suneditor-content.css +646 -0
  11. package/src/assets/suneditor.css +3378 -0
  12. package/src/core/base/eventHandlers/handler_toolbar.js +114 -0
  13. package/src/core/base/eventHandlers/handler_ww_clipboard.js +31 -0
  14. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +69 -0
  15. package/src/core/base/eventHandlers/handler_ww_key_input.js +975 -0
  16. package/src/core/base/eventHandlers/handler_ww_mouse.js +118 -0
  17. package/src/core/base/eventManager.js +1115 -0
  18. package/src/core/base/events.js +320 -0
  19. package/src/core/base/history.js +301 -0
  20. package/src/core/class/char.js +146 -0
  21. package/src/core/class/component.js +627 -0
  22. package/src/core/class/format.js +3255 -0
  23. package/src/core/class/html.js +1621 -0
  24. package/src/core/class/menu.js +260 -0
  25. package/src/core/class/nodeTransform.js +379 -0
  26. package/src/core/class/notice.js +42 -0
  27. package/src/core/class/offset.js +578 -0
  28. package/src/core/class/selection.js +508 -0
  29. package/src/core/class/shortcuts.js +38 -0
  30. package/src/core/class/toolbar.js +440 -0
  31. package/src/core/class/viewer.js +646 -0
  32. package/src/core/editor.js +1593 -0
  33. package/src/core/section/actives.js +107 -0
  34. package/src/core/section/constructor.js +1237 -0
  35. package/src/core/section/context.js +97 -0
  36. package/src/editorInjector/_classes.js +22 -0
  37. package/src/editorInjector/_core.js +28 -0
  38. package/src/editorInjector/index.js +13 -0
  39. package/src/helper/converter.js +313 -0
  40. package/src/helper/domUtils.js +1177 -0
  41. package/src/helper/env.js +250 -0
  42. package/src/helper/index.js +19 -0
  43. package/src/helper/numbers.js +68 -0
  44. package/src/helper/unicode.js +43 -0
  45. package/src/langs/ckb.js +161 -0
  46. package/src/langs/cs.js +161 -0
  47. package/src/langs/da.js +161 -0
  48. package/src/langs/de.js +162 -0
  49. package/src/langs/en.js +199 -0
  50. package/src/langs/es.js +162 -0
  51. package/src/langs/fa.js +159 -0
  52. package/src/langs/fr.js +161 -0
  53. package/src/langs/he.js +162 -0
  54. package/src/{lang → langs}/index.js +0 -2
  55. package/src/langs/it.js +162 -0
  56. package/src/langs/ja.js +162 -0
  57. package/src/langs/ko.js +162 -0
  58. package/src/langs/lv.js +162 -0
  59. package/src/langs/nl.js +162 -0
  60. package/src/langs/pl.js +162 -0
  61. package/src/langs/pt_br.js +162 -0
  62. package/src/langs/ro.js +162 -0
  63. package/src/langs/ru.js +162 -0
  64. package/src/langs/se.js +162 -0
  65. package/src/langs/tr.js +159 -0
  66. package/src/langs/ua.js +162 -0
  67. package/src/langs/ur.js +162 -0
  68. package/src/langs/zh_cn.js +162 -0
  69. package/src/modules/ApiManager.js +168 -0
  70. package/src/modules/ColorPicker.js +302 -0
  71. package/src/modules/Controller.js +315 -0
  72. package/src/modules/Figure.js +1160 -0
  73. package/src/modules/FileBrowser.js +271 -0
  74. package/src/modules/FileManager.js +290 -0
  75. package/src/modules/HueSlider.js +513 -0
  76. package/src/modules/Modal.js +177 -0
  77. package/src/modules/ModalAnchorEditor.js +494 -0
  78. package/src/modules/SelectMenu.js +447 -0
  79. package/src/modules/_DragHandle.js +16 -0
  80. package/src/modules/index.js +14 -0
  81. package/src/plugins/command/blockquote.js +47 -47
  82. package/src/plugins/command/exportPdf.js +168 -0
  83. package/src/plugins/command/fileUpload.js +389 -0
  84. package/src/plugins/command/list_bulleted.js +112 -0
  85. package/src/plugins/command/list_numbered.js +115 -0
  86. package/src/plugins/dropdown/align.js +143 -0
  87. package/src/plugins/dropdown/backgroundColor.js +73 -0
  88. package/src/plugins/dropdown/font.js +113 -0
  89. package/src/plugins/dropdown/fontColor.js +73 -0
  90. package/src/plugins/dropdown/formatBlock.js +141 -0
  91. package/src/plugins/dropdown/hr.js +111 -0
  92. package/src/plugins/dropdown/layout.js +72 -0
  93. package/src/plugins/dropdown/lineHeight.js +114 -0
  94. package/src/plugins/dropdown/list.js +107 -0
  95. package/src/plugins/dropdown/paragraphStyle.js +117 -0
  96. package/src/plugins/dropdown/table.js +2810 -0
  97. package/src/plugins/dropdown/template.js +71 -0
  98. package/src/plugins/dropdown/textStyle.js +137 -0
  99. package/src/plugins/field/mention.js +172 -0
  100. package/src/plugins/fileBrowser/imageGallery.js +76 -59
  101. package/src/plugins/index.js +86 -24
  102. package/src/plugins/input/fontSize.js +357 -0
  103. package/src/plugins/modal/audio.js +510 -0
  104. package/src/plugins/modal/image.js +1062 -0
  105. package/src/plugins/modal/link.js +211 -0
  106. package/src/plugins/modal/math.js +347 -0
  107. package/src/plugins/modal/video.js +870 -0
  108. package/src/suneditor.js +62 -67
  109. package/src/themes/test.css +61 -0
  110. package/typings/CommandPlugin.d.ts +8 -0
  111. package/typings/DialogPlugin.d.ts +20 -0
  112. package/typings/FileBrowserPlugin.d.ts +30 -0
  113. package/typings/Lang.d.ts +124 -0
  114. package/typings/Module.d.ts +15 -0
  115. package/typings/Plugin.d.ts +42 -0
  116. package/typings/SubmenuPlugin.d.ts +8 -0
  117. package/typings/_classes.d.ts +17 -0
  118. package/typings/_colorPicker.d.ts +60 -0
  119. package/typings/_core.d.ts +55 -0
  120. package/typings/align.d.ts +5 -0
  121. package/{src/plugins/dialog → typings}/audio.d.ts +1 -1
  122. package/typings/backgroundColor.d.ts +5 -0
  123. package/{src/plugins/command → typings}/blockquote.d.ts +1 -1
  124. package/typings/char.d.ts +39 -0
  125. package/typings/component.d.ts +38 -0
  126. package/typings/context.d.ts +39 -0
  127. package/typings/converter.d.ts +33 -0
  128. package/typings/dialog.d.ts +28 -0
  129. package/typings/domUtils.d.ts +361 -0
  130. package/typings/editor.d.ts +7 -0
  131. package/typings/editor.ts +542 -0
  132. package/typings/env.d.ts +70 -0
  133. package/typings/eventManager.d.ts +37 -0
  134. package/typings/events.d.ts +262 -0
  135. package/typings/fileBrowser.d.ts +42 -0
  136. package/typings/fileManager.d.ts +67 -0
  137. package/typings/font.d.ts +5 -0
  138. package/typings/fontColor.d.ts +5 -0
  139. package/typings/fontSize.d.ts +5 -0
  140. package/typings/format.d.ts +191 -0
  141. package/typings/formatBlock.d.ts +5 -0
  142. package/typings/history.d.ts +48 -0
  143. package/typings/horizontalRule.d.ts +5 -0
  144. package/{src/plugins/dialog → typings}/image.d.ts +1 -1
  145. package/{src/plugins/fileBrowser → typings}/imageGallery.d.ts +1 -1
  146. package/typings/index.d.ts +21 -0
  147. package/{src/plugins/modules/index.d.ts → typings/index.modules.d.ts} +3 -3
  148. package/typings/index.plugins.d.ts +58 -0
  149. package/typings/lineHeight.d.ts +5 -0
  150. package/{src/plugins/dialog → typings}/link.d.ts +1 -1
  151. package/typings/list.d.ts +5 -0
  152. package/{src/plugins/dialog → typings}/math.d.ts +1 -1
  153. package/typings/mediaContainer.d.ts +25 -0
  154. package/typings/node.d.ts +57 -0
  155. package/typings/notice.d.ts +16 -0
  156. package/typings/numbers.d.ts +29 -0
  157. package/typings/offset.d.ts +24 -0
  158. package/typings/options.d.ts +589 -0
  159. package/typings/paragraphStyle.d.ts +5 -0
  160. package/typings/resizing.d.ts +141 -0
  161. package/typings/selection.d.ts +94 -0
  162. package/typings/shortcuts.d.ts +13 -0
  163. package/typings/suneditor.d.ts +9 -0
  164. package/typings/table.d.ts +5 -0
  165. package/typings/template.d.ts +5 -0
  166. package/typings/textStyle.d.ts +5 -0
  167. package/typings/toolbar.d.ts +32 -0
  168. package/typings/unicode.d.ts +25 -0
  169. package/{src/plugins/dialog → typings}/video.d.ts +1 -1
  170. package/dist/css/suneditor.min.css +0 -1
  171. package/src/assets/css/suneditor-contents.css +0 -562
  172. package/src/assets/css/suneditor.css +0 -566
  173. package/src/assets/defaultIcons.js +0 -103
  174. package/src/lang/Lang.d.ts +0 -144
  175. package/src/lang/ckb.d.ts +0 -5
  176. package/src/lang/ckb.js +0 -188
  177. package/src/lang/cs.d.ts +0 -5
  178. package/src/lang/cs.js +0 -188
  179. package/src/lang/da.d.ts +0 -5
  180. package/src/lang/da.js +0 -191
  181. package/src/lang/de.d.ts +0 -5
  182. package/src/lang/de.js +0 -188
  183. package/src/lang/en.d.ts +0 -5
  184. package/src/lang/en.js +0 -188
  185. package/src/lang/es.d.ts +0 -5
  186. package/src/lang/es.js +0 -188
  187. package/src/lang/fa.d.ts +0 -5
  188. package/src/lang/fa.js +0 -188
  189. package/src/lang/fr.d.ts +0 -5
  190. package/src/lang/fr.js +0 -188
  191. package/src/lang/he.d.ts +0 -5
  192. package/src/lang/he.js +0 -188
  193. package/src/lang/index.d.ts +0 -23
  194. package/src/lang/it.d.ts +0 -5
  195. package/src/lang/it.js +0 -188
  196. package/src/lang/ja.d.ts +0 -5
  197. package/src/lang/ja.js +0 -188
  198. package/src/lang/ko.d.ts +0 -5
  199. package/src/lang/ko.js +0 -188
  200. package/src/lang/lv.d.ts +0 -5
  201. package/src/lang/lv.js +0 -188
  202. package/src/lang/nl.d.ts +0 -5
  203. package/src/lang/nl.js +0 -188
  204. package/src/lang/pl.d.ts +0 -5
  205. package/src/lang/pl.js +0 -188
  206. package/src/lang/pt_br.d.ts +0 -5
  207. package/src/lang/pt_br.js +0 -189
  208. package/src/lang/ro.d.ts +0 -5
  209. package/src/lang/ro.js +0 -188
  210. package/src/lang/ru.d.ts +0 -5
  211. package/src/lang/ru.js +0 -188
  212. package/src/lang/se.d.ts +0 -5
  213. package/src/lang/se.js +0 -191
  214. package/src/lang/tr.d.ts +0 -5
  215. package/src/lang/tr.js +0 -191
  216. package/src/lang/ua.d.ts +0 -5
  217. package/src/lang/ua.js +0 -188
  218. package/src/lang/ur.d.ts +0 -5
  219. package/src/lang/ur.js +0 -188
  220. package/src/lang/zh_cn.d.ts +0 -5
  221. package/src/lang/zh_cn.js +0 -187
  222. package/src/lib/constructor.js +0 -954
  223. package/src/lib/context.d.ts +0 -42
  224. package/src/lib/context.js +0 -71
  225. package/src/lib/core.d.ts +0 -1135
  226. package/src/lib/core.js +0 -9395
  227. package/src/lib/history.d.ts +0 -48
  228. package/src/lib/history.js +0 -219
  229. package/src/lib/util.d.ts +0 -678
  230. package/src/lib/util.js +0 -2131
  231. package/src/options.d.ts +0 -608
  232. package/src/plugins/CommandPlugin.d.ts +0 -8
  233. package/src/plugins/DialogPlugin.d.ts +0 -20
  234. package/src/plugins/FileBrowserPlugin.d.ts +0 -30
  235. package/src/plugins/Module.d.ts +0 -15
  236. package/src/plugins/Plugin.d.ts +0 -42
  237. package/src/plugins/SubmenuPlugin.d.ts +0 -8
  238. package/src/plugins/dialog/audio.js +0 -559
  239. package/src/plugins/dialog/image.js +0 -1126
  240. package/src/plugins/dialog/link.js +0 -223
  241. package/src/plugins/dialog/math.js +0 -295
  242. package/src/plugins/dialog/mention.js +0 -242
  243. package/src/plugins/dialog/video.js +0 -979
  244. package/src/plugins/index.d.ts +0 -79
  245. package/src/plugins/modules/_anchor.js +0 -461
  246. package/src/plugins/modules/_colorPicker.d.ts +0 -60
  247. package/src/plugins/modules/_colorPicker.js +0 -201
  248. package/src/plugins/modules/_notice.d.ts +0 -21
  249. package/src/plugins/modules/_notice.js +0 -72
  250. package/src/plugins/modules/_selectMenu.js +0 -119
  251. package/src/plugins/modules/component.d.ts +0 -25
  252. package/src/plugins/modules/component.js +0 -81
  253. package/src/plugins/modules/dialog.d.ts +0 -28
  254. package/src/plugins/modules/dialog.js +0 -175
  255. package/src/plugins/modules/fileBrowser.d.ts +0 -42
  256. package/src/plugins/modules/fileBrowser.js +0 -374
  257. package/src/plugins/modules/fileManager.d.ts +0 -67
  258. package/src/plugins/modules/fileManager.js +0 -326
  259. package/src/plugins/modules/index.js +0 -9
  260. package/src/plugins/modules/resizing.d.ts +0 -154
  261. package/src/plugins/modules/resizing.js +0 -903
  262. package/src/plugins/submenu/align.d.ts +0 -5
  263. package/src/plugins/submenu/align.js +0 -160
  264. package/src/plugins/submenu/font.d.ts +0 -5
  265. package/src/plugins/submenu/font.js +0 -123
  266. package/src/plugins/submenu/fontColor.d.ts +0 -5
  267. package/src/plugins/submenu/fontColor.js +0 -101
  268. package/src/plugins/submenu/fontSize.d.ts +0 -5
  269. package/src/plugins/submenu/fontSize.js +0 -112
  270. package/src/plugins/submenu/formatBlock.d.ts +0 -5
  271. package/src/plugins/submenu/formatBlock.js +0 -273
  272. package/src/plugins/submenu/hiliteColor.d.ts +0 -5
  273. package/src/plugins/submenu/hiliteColor.js +0 -102
  274. package/src/plugins/submenu/horizontalRule.d.ts +0 -5
  275. package/src/plugins/submenu/horizontalRule.js +0 -98
  276. package/src/plugins/submenu/lineHeight.d.ts +0 -5
  277. package/src/plugins/submenu/lineHeight.js +0 -104
  278. package/src/plugins/submenu/list.d.ts +0 -5
  279. package/src/plugins/submenu/list.js +0 -456
  280. package/src/plugins/submenu/paragraphStyle.d.ts +0 -5
  281. package/src/plugins/submenu/paragraphStyle.js +0 -135
  282. package/src/plugins/submenu/table.d.ts +0 -5
  283. package/src/plugins/submenu/table.js +0 -1431
  284. package/src/plugins/submenu/template.d.ts +0 -5
  285. package/src/plugins/submenu/template.js +0 -72
  286. package/src/plugins/submenu/textStyle.d.ts +0 -5
  287. package/src/plugins/submenu/textStyle.js +0 -167
  288. package/src/suneditor.d.ts +0 -9
  289. package/src/suneditor_build.js +0 -18
  290. /package/{src/plugins/dialog → typings}/mention.d.ts +0 -0
@@ -0,0 +1,1621 @@
1
+ /**
2
+ * @fileoverview Char class
3
+ */
4
+
5
+ import CoreInjector from '../../editorInjector/_core';
6
+ import { domUtils, converter, numbers, unicode, env } from '../../helper';
7
+
8
+ const REQUIRED_DATA_ATTRS = 'data-se-[^\\s]+';
9
+
10
+ const HTML = function (editor) {
11
+ CoreInjector.call(this, editor);
12
+
13
+ // members
14
+ const options = this.options;
15
+ this._isAllowedClassName = function (v) {
16
+ return this.test(v) ? v : '';
17
+ }.bind(this.options.get('allowedClassName'));
18
+ this._allowHTMLComment = null;
19
+ this._disallowedStyleNodesRegExp = null;
20
+ this._htmlCheckWhitelistRegExp = null;
21
+ this._htmlCheckBlacklistRegExp = null;
22
+ this._elementWhitelistRegExp = null;
23
+ this._elementBlacklistRegExp = null;
24
+ this._attributeWhitelist = null;
25
+ this._attributeWhitelistRegExp = null;
26
+ this._attributeBlacklist = null;
27
+ this._attributeBlacklistRegExp = null;
28
+ this._textStyleTags = options.get('_textStyleTags');
29
+
30
+ // clean styles
31
+ const tagStyles = options.get('tagStyles');
32
+ const splitTagStyles = {};
33
+ for (const k in tagStyles) {
34
+ const s = k.split('|');
35
+ for (let i = 0, len = s.length, n; i < len; i++) {
36
+ n = s[i];
37
+ if (!splitTagStyles[n]) splitTagStyles[n] = '';
38
+ else splitTagStyles[n] += '|';
39
+ splitTagStyles[n] += tagStyles[k];
40
+ }
41
+ }
42
+ for (const k in splitTagStyles) {
43
+ splitTagStyles[k] = new RegExp(`\\s*[^-a-zA-Z](${splitTagStyles[k]})\\s*:[^;]+(?!;)*`, 'gi');
44
+ }
45
+
46
+ const stylesMap = new Map();
47
+ const stylesObj = {
48
+ ...splitTagStyles,
49
+ line: options.get('_lineStylesRegExp')
50
+ };
51
+ this._textStyleTags.forEach((v) => {
52
+ stylesObj[v] = options.get('_textStylesRegExp');
53
+ });
54
+
55
+ for (const key in stylesObj) {
56
+ stylesMap.set(new RegExp(`^(${key})$`), stylesObj[key]);
57
+ }
58
+ this._cleanStyleTagKeyRegExp = new RegExp(`^(${Object.keys(stylesObj).join('|')})$`, 'i');
59
+ this._cleanStyleRegExpMap = stylesMap;
60
+
61
+ // font size unit
62
+ this.fontSizeUnitRegExp = new RegExp('\\d+(' + this.options.get('fontSizeUnits').join('|') + ')$', 'i');
63
+
64
+ // extra tags
65
+ const allowedExtraTags = options.get('_allowedExtraTag');
66
+ const disallowedExtraTags = options.get('_disallowedExtraTag');
67
+ this.__disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
68
+ this.__disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
69
+ this.__allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
70
+
71
+ // set disallow text nodes
72
+ const disallowStyleNodes = Object.keys(options.get('_defaultStyleTagMap'));
73
+ const allowStyleNodes = !options.get('elementWhitelist')
74
+ ? []
75
+ : options
76
+ .get('elementWhitelist')
77
+ .split('|')
78
+ .filter((v) => /b|i|ins|s|strike/i.test(v));
79
+ for (let i = 0; i < allowStyleNodes.length; i++) {
80
+ disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
81
+ }
82
+ this._disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
83
+
84
+ // whitelist
85
+ // tags
86
+ const defaultAttr = options.get('__defaultAttributeWhitelist');
87
+ this._allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
88
+ // html check
89
+ this._htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
90
+ this._htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
91
+ // elements
92
+ this._elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
93
+ this._elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
94
+ // attributes
95
+ const regEndStr = '\\s*=\\s*(")[^"]*\\1';
96
+ const _wAttr = options.get('attributeWhitelist');
97
+ let tagsAttr = {};
98
+ let allAttr = '';
99
+ if (_wAttr) {
100
+ for (const k in _wAttr) {
101
+ if (/^on[a-z]+$/i.test(_wAttr[k])) continue;
102
+ if (k === '*') {
103
+ allAttr = GetRegList(_wAttr[k], defaultAttr);
104
+ } else {
105
+ tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_wAttr[k], '') + ')' + regEndStr, 'ig');
106
+ }
107
+ }
108
+ }
109
+
110
+ this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + ')' + regEndStr, 'ig');
111
+ this._attributeWhitelist = tagsAttr;
112
+
113
+ // blacklist
114
+ const _bAttr = options.get('attributeBlacklist');
115
+ tagsAttr = {};
116
+ allAttr = '';
117
+ if (_bAttr) {
118
+ for (const k in _bAttr) {
119
+ if (k === '*') {
120
+ allAttr = GetRegList(_bAttr[k], '');
121
+ } else {
122
+ tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_bAttr[k], '') + ')' + regEndStr, 'ig');
123
+ }
124
+ }
125
+ }
126
+
127
+ this._attributeBlacklistRegExp = new RegExp('\\s(?:' + (allAttr || '^') + ')' + regEndStr, 'ig');
128
+ this._attributeBlacklist = tagsAttr;
129
+ };
130
+
131
+ HTML.prototype = {
132
+ /**
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}
141
+ */
142
+ clean(html, requireFormat, whitelist, blacklist) {
143
+ const { tagFilter, formatFilter, classFilter, styleNodeFilter, attrFilter, styleFilter } = this.options.get('strictMode');
144
+ let cleanData = '';
145
+
146
+ html = this.compress(html);
147
+
148
+ if (tagFilter) {
149
+ html = this._deleteDisallowedTags(html, this._elementWhitelistRegExp, this._elementBlacklistRegExp).replace(/<br\/?>$/i, '');
150
+ }
151
+
152
+ if (attrFilter || styleFilter) {
153
+ html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, CleanElements.bind(this, attrFilter, styleFilter));
154
+ }
155
+
156
+ // get dom tree
157
+ const dom = this._d.createRange().createContextualFragment(html, true);
158
+
159
+ if (tagFilter) {
160
+ try {
161
+ this._consistencyCheckOfHTML(dom, this._htmlCheckWhitelistRegExp, this._htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter);
162
+ } catch (error) {
163
+ console.warn('[SUNEDITOR.html.clean.fail]', error.message);
164
+ }
165
+ }
166
+
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
+ });
173
+
174
+ if (formatFilter) {
175
+ let domTree = dom.childNodes;
176
+ if (!requireFormat) requireFormat = this._isFormatData(domTree);
177
+ if (requireFormat) domTree = this._editFormat(dom).childNodes;
178
+
179
+ for (let i = 0, len = domTree.length, t; i < len; i++) {
180
+ t = domTree[i];
181
+ if (this.__allowedTagNameRegExp.test(t.nodeName)) {
182
+ cleanData += t.outerHTML;
183
+ continue;
184
+ }
185
+ cleanData += this._makeLine(t, requireFormat);
186
+ }
187
+ }
188
+
189
+ // set clean data
190
+ if (!cleanData) cleanData = html;
191
+
192
+ // whitelist, blacklist
193
+ if (tagFilter) {
194
+ if (whitelist) cleanData = cleanData.replace(typeof whitelist === 'string' ? converter.createElementWhitelist(whitelist) : whitelist, '');
195
+ if (blacklist) cleanData = cleanData.replace(typeof blacklist === 'string' ? converter.createElementBlacklist(blacklist) : blacklist, '');
196
+ }
197
+
198
+ if (styleNodeFilter) {
199
+ cleanData = this._styleNodeConvertor(cleanData);
200
+ }
201
+
202
+ return cleanData;
203
+ },
204
+
205
+ /**
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.
212
+ */
213
+ insert(html, rangeSelection, notCheckCharCount, notCleanData) {
214
+ if (!this.editor.frameContext.get('wysiwygFrame').contains(this.selection.get().focusNode)) this.editor.focus();
215
+
216
+ if (typeof html === 'string') {
217
+ if (!notCleanData) html = this.clean(html, false, null, null);
218
+ 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;
222
+ if (this._isFormatData(domTree)) html = this._convertListCell(domTree);
223
+ }
224
+
225
+ const dom = this._d.createRange().createContextualFragment(html);
226
+ const domTree = dom.childNodes;
227
+
228
+ if (!notCheckCharCount) {
229
+ const type = this.editor.frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
230
+ let checkHTML = '';
231
+ for (let i = 0, len = domTree.length; i < len; i++) {
232
+ checkHTML += domTree[i][type];
233
+ }
234
+ if (!this.char.check(checkHTML)) return;
235
+ }
236
+
237
+ let c, a, t, prev, firstCon;
238
+ while ((c = domTree[0])) {
239
+ if (prev?.nodeType === 3 && a?.nodeType === 1 && domUtils.isBreak(c)) {
240
+ prev = c;
241
+ domUtils.removeItem(c);
242
+ continue;
243
+ }
244
+ t = this.insertNode(c, a, true);
245
+ a = t.container || t;
246
+ if (!firstCon) firstCon = t;
247
+ prev = c;
248
+ }
249
+
250
+ if (prev?.nodeType === 3 && a?.nodeType === 1) a = prev;
251
+ const offset = a.nodeType === 3 ? t.endOffset || a.textContent.length : a.childNodes.length;
252
+
253
+ if (rangeSelection) {
254
+ this.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
255
+ } else if (!this.component.is(a)) {
256
+ this.selection.setRange(a, offset, a, offset);
257
+ }
258
+ } catch (error) {
259
+ if (this.editor.frameContext.get('isReadOnly') || this.editor.frameContext.get('isDisabled')) return;
260
+ throw Error('[SUNEDITOR.html.insert.error]', error.message);
261
+ }
262
+ } else {
263
+ if (this.component.is(html)) {
264
+ this.component.insert(html, notCheckCharCount, false);
265
+ } else {
266
+ let afterNode = null;
267
+ if (this.format.isLine(html) || domUtils.isMedia(html)) {
268
+ afterNode = this.format.getLine(this.selection.getNode(), null);
269
+ }
270
+ this.insertNode(html, afterNode, notCheckCharCount);
271
+ }
272
+ }
273
+
274
+ this.editor.effectNode = null;
275
+ this.editor.focus();
276
+ this.history.push(false);
277
+ },
278
+
279
+ /**
280
+ * @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 }".
283
+ * @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.
286
+ * @returns {Object|Node|null}
287
+ */
288
+ insertNode(oNode, afterNode, notCheckCharCount) {
289
+ let result = null;
290
+ if (this.editor.frameContext.get('isReadOnly') || (!notCheckCharCount && !this.char.check(oNode, null))) {
291
+ return result;
292
+ }
293
+
294
+ 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));
297
+
298
+ let parentNode,
299
+ originAfter,
300
+ tempAfterNode,
301
+ tempParentNode = null;
302
+ const freeFormat = this.format.isBrLine(line);
303
+ const isFormats = (!freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) || this.component.isBasic(oNode);
304
+
305
+ if (insertListCell) {
306
+ tempAfterNode = afterNode || domUtils.isList(oNode) ? line.lastChild : line.nextElementSibling;
307
+ tempParentNode = domUtils.isList(oNode) ? line : (tempAfterNode || line).parentNode;
308
+ }
309
+
310
+ if (!afterNode && (isFormats || this.component.isBasic(oNode) || domUtils.isMedia(oNode))) {
311
+ const isEdge = domUtils.isEdgePoint(range.endContainer, range.endOffset, 'end');
312
+ const r = this.remove();
313
+ const container = r.container;
314
+ const prevContainer = container === r.prevContainer && range.collapsed ? null : r.prevContainer;
315
+
316
+ if (insertListCell && prevContainer) {
317
+ tempParentNode = prevContainer.nodeType === 3 ? prevContainer.parentNode : prevContainer;
318
+ if (tempParentNode.contains(container)) {
319
+ let sameParent = true;
320
+ tempAfterNode = container;
321
+ while (tempAfterNode.parentNode && tempAfterNode.parentNode !== tempParentNode) {
322
+ tempAfterNode = tempAfterNode.parentNode;
323
+ sameParent = false;
324
+ }
325
+ if (sameParent && container === prevContainer) tempAfterNode = tempAfterNode.nextSibling;
326
+ } else {
327
+ tempAfterNode = null;
328
+ }
329
+ } else if (insertListCell && domUtils.isListCell(container) && !line.parentElement) {
330
+ line = domUtils.createElement('LI');
331
+ tempParentNode.appendChild(line);
332
+ container.appendChild(tempParentNode);
333
+ 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);
337
+ });
338
+ afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 : domUtils.getNodeDepth(depthFormat) + 1);
339
+ if (!afterNode) {
340
+ tempAfterNode = afterNode = line;
341
+ } else if (insertListCell) {
342
+ if (line.contains(container)) {
343
+ const subList = domUtils.isList(line.lastElementChild);
344
+ let newCell = null;
345
+ if (!isEdge) {
346
+ newCell = line.cloneNode(false);
347
+ newCell.appendChild(afterNode.textContent.trim() ? afterNode : domUtils.createTextNode(unicode.zeroWidthSpace));
348
+ }
349
+ if (subList) {
350
+ if (!newCell) {
351
+ newCell = line.cloneNode(false);
352
+ newCell.appendChild(domUtils.createTextNode(unicode.zeroWidthSpace));
353
+ }
354
+ newCell.appendChild(line.lastElementChild);
355
+ }
356
+ if (newCell) {
357
+ line.parentNode.insertBefore(newCell, line.nextElementSibling);
358
+ tempAfterNode = afterNode = newCell;
359
+ }
360
+ }
361
+ } else {
362
+ afterNode = afterNode.previousSibling;
363
+ }
364
+ }
365
+ }
366
+
367
+ range = !afterNode && !isFormats ? this.selection.getRangeAndAddLine(this.selection.getRange(), null) : this.selection.getRange();
368
+ const commonCon = range.commonAncestorContainer;
369
+ const startOff = range.startOffset;
370
+ const endOff = range.endOffset;
371
+ const formatRange = range.startContainer === commonCon && this.format.isLine(commonCon);
372
+ const startCon = formatRange ? commonCon.childNodes[startOff] || commonCon.childNodes[0] || range.startContainer : range.startContainer;
373
+ const endCon = formatRange ? commonCon.childNodes[endOff] || commonCon.childNodes[commonCon.childNodes.length - 1] || range.endContainer : range.endContainer;
374
+
375
+ if (!insertListCell) {
376
+ if (!afterNode) {
377
+ parentNode = startCon;
378
+ if (startCon.nodeType === 3) {
379
+ parentNode = startCon.parentNode;
380
+ }
381
+
382
+ /** No Select range node */
383
+ if (range.collapsed) {
384
+ if (commonCon.nodeType === 3) {
385
+ if (commonCon.textContent.length > endOff) afterNode = commonCon.splitText(endOff);
386
+ else afterNode = commonCon.nextSibling;
387
+ } else {
388
+ if (!domUtils.isBreak(parentNode)) {
389
+ const c = parentNode.childNodes[startOff];
390
+ const focusNode = c?.nodeType === 3 && domUtils.isZeroWith(c) && domUtils.isBreak(c.nextSibling) ? c.nextSibling : c;
391
+ if (focusNode) {
392
+ if (!focusNode.nextSibling && domUtils.isBreak(focusNode)) {
393
+ parentNode.removeChild(focusNode);
394
+ afterNode = null;
395
+ } else {
396
+ afterNode = domUtils.isBreak(focusNode) && !domUtils.isBreak(oNode) ? focusNode : focusNode.nextSibling;
397
+ }
398
+ } else {
399
+ afterNode = null;
400
+ }
401
+ } else {
402
+ afterNode = parentNode;
403
+ parentNode = parentNode.parentNode;
404
+ }
405
+ }
406
+ } else {
407
+ /** Select range nodes */
408
+ const isSameContainer = startCon === endCon;
409
+
410
+ if (isSameContainer) {
411
+ if (domUtils.isEdgePoint(endCon, endOff)) afterNode = endCon.nextSibling;
412
+ else afterNode = endCon.splitText(endOff);
413
+
414
+ let removeNode = startCon;
415
+ if (!domUtils.isEdgePoint(startCon, startOff)) removeNode = startCon.splitText(startOff);
416
+
417
+ parentNode.removeChild(removeNode);
418
+ if (parentNode.childNodes.length === 0 && isFormats) {
419
+ parentNode.innerHTML = '<br>';
420
+ }
421
+ } else {
422
+ const removedTag = this.remove();
423
+ const container = removedTag.container;
424
+ const prevContainer = removedTag.prevContainer;
425
+
426
+ if (container?.childNodes.length === 0 && isFormats) {
427
+ if (this.format.isLine(container)) {
428
+ container.innerHTML = '<br>';
429
+ } else if (this.format.isBlock(container)) {
430
+ container.innerHTML = '<' + this.options.get('defaultLine') + '><br></' + this.options.get('defaultLine') + '>';
431
+ }
432
+ }
433
+
434
+ if (domUtils.isListCell(container) && oNode.nodeType === 3) {
435
+ parentNode = container;
436
+ afterNode = null;
437
+ } else if (!isFormats && prevContainer) {
438
+ parentNode = prevContainer.nodeType === 3 ? prevContainer.parentNode : prevContainer;
439
+ if (parentNode.contains(container)) {
440
+ let sameParent = true;
441
+ afterNode = container;
442
+ while (afterNode.parentNode && afterNode.parentNode !== parentNode) {
443
+ afterNode = afterNode.parentNode;
444
+ sameParent = false;
445
+ }
446
+ if (sameParent && container === prevContainer) afterNode = afterNode.nextSibling;
447
+ } else {
448
+ afterNode = null;
449
+ }
450
+ } else if (domUtils.isWysiwygFrame(container) && !this.format.isLine(oNode)) {
451
+ parentNode = container.appendChild(domUtils.createElement(this.options.get('defaultLine')));
452
+ afterNode = null;
453
+ } else {
454
+ afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
455
+ parentNode = !afterNode || !afterNode.parentNode ? commonCon : afterNode.parentNode;
456
+ }
457
+
458
+ while (afterNode && !this.format.isLine(afterNode) && afterNode.parentNode !== commonCon) {
459
+ afterNode = afterNode.parentNode;
460
+ }
461
+ }
462
+ }
463
+ } else {
464
+ // has afterNode
465
+ parentNode = afterNode.parentNode;
466
+ afterNode = afterNode.nextSibling;
467
+ originAfter = true;
468
+ }
469
+ }
470
+
471
+ try {
472
+ // set node
473
+ const wysiwyg = this.editor.frameContext.get('wysiwyg');
474
+ if (!insertListCell) {
475
+ if (domUtils.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
476
+ parentNode = wysiwyg;
477
+ afterNode = null;
478
+ }
479
+
480
+ if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!domUtils.isListCell(parentNode) && this.component.isBasic(oNode))) {
481
+ const oldParent = parentNode;
482
+ if (domUtils.isList(afterNode)) {
483
+ parentNode = afterNode;
484
+ afterNode = null;
485
+ } else if (domUtils.isListCell(afterNode)) {
486
+ parentNode = afterNode.previousElementSibling || afterNode;
487
+ } else if (!originAfter && !afterNode) {
488
+ 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);
491
+ parentNode = rangeCon ? container : container.parentNode;
492
+ afterNode = rangeCon ? null : container.nextSibling;
493
+ }
494
+
495
+ if (oldParent.childNodes.length === 0 && parentNode !== oldParent) domUtils.removeItem(oldParent);
496
+ }
497
+
498
+ if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !domUtils.isListCell(parentNode) && !domUtils.isWysiwygFrame(parentNode)) {
499
+ afterNode = parentNode.nextElementSibling;
500
+ parentNode = parentNode.parentNode;
501
+ }
502
+
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;
506
+ }
507
+ }
508
+
509
+ // insert--
510
+ if (insertListCell) {
511
+ if (!tempParentNode.parentNode) {
512
+ parentNode = wysiwyg;
513
+ afterNode = null;
514
+ } else {
515
+ parentNode = tempParentNode;
516
+ afterNode = tempAfterNode;
517
+ }
518
+ } else {
519
+ afterNode = parentNode === afterNode ? parentNode.lastChild : afterNode;
520
+ }
521
+
522
+ if (domUtils.isListCell(oNode) && !domUtils.isList(parentNode)) {
523
+ if (domUtils.isListCell(parentNode)) {
524
+ afterNode = parentNode.nextElementSibling;
525
+ parentNode = parentNode.parentNode;
526
+ } else {
527
+ const ul = domUtils.createElement('ol');
528
+ parentNode.insertBefore(ul, afterNode);
529
+ parentNode = ul;
530
+ afterNode = null;
531
+ }
532
+ insertListCell = true;
533
+ }
534
+
535
+ this._checkDuplicateNode(oNode, parentNode);
536
+ parentNode.insertBefore(oNode, afterNode);
537
+
538
+ if (insertListCell) {
539
+ if (domUtils.isZeroWith(line.textContent.trim())) {
540
+ domUtils.removeItem(line);
541
+ oNode = oNode.lastChild;
542
+ } else {
543
+ const chList = domUtils.getArrayItem(line.children, domUtils.isList);
544
+ if (chList) {
545
+ if (oNode !== chList) {
546
+ oNode.appendChild(chList);
547
+ oNode = chList.previousSibling;
548
+ } else {
549
+ parentNode.appendChild(oNode);
550
+ oNode = parentNode;
551
+ }
552
+
553
+ if (domUtils.isZeroWith(line.textContent.trim())) {
554
+ domUtils.removeItem(line);
555
+ }
556
+ }
557
+ }
558
+ }
559
+ } catch (error) {
560
+ parentNode.appendChild(oNode);
561
+ console.warn('[SUNEDITOR.html.insertNode.warn]', error);
562
+ } finally {
563
+ const dupleNodes = parentNode.querySelectorAll('[data-duple]');
564
+ if (dupleNodes.length > 0) {
565
+ for (let i = 0, len = dupleNodes.length, d, c, ch, parent; i < len; i++) {
566
+ d = dupleNodes[i];
567
+ ch = d.childNodes;
568
+ parent = d.parentNode;
569
+
570
+ while (ch[0]) {
571
+ c = ch[0];
572
+ parent.insertBefore(c, d);
573
+ }
574
+
575
+ if (d === oNode) oNode = c;
576
+ domUtils.removeItem(d);
577
+ }
578
+ }
579
+
580
+ if ((this.format.isLine(oNode) || this.component.isBasic(oNode)) && startCon === endCon) {
581
+ const cItem = this.format.getLine(commonCon, null);
582
+ if (cItem?.nodeType === 1 && domUtils.isEmptyLine(cItem)) {
583
+ domUtils.removeItem(cItem);
584
+ }
585
+ }
586
+
587
+ if (freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) {
588
+ oNode = this._setIntoFreeFormat(oNode);
589
+ }
590
+
591
+ if (!this.component.isBasic(oNode)) {
592
+ let offset = 1;
593
+ 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)) {
619
+ let zeroWidth = null;
620
+ if (!oNode.previousSibling || domUtils.isBreak(oNode.previousSibling)) {
621
+ zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
622
+ oNode.parentNode.insertBefore(zeroWidth, oNode);
623
+ }
624
+
625
+ if (!oNode.nextSibling || domUtils.isBreak(oNode.nextSibling)) {
626
+ zeroWidth = domUtils.createTextNode(unicode.zeroWidthSpace);
627
+ oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
628
+ }
629
+
630
+ if (this.format._isIgnoreNodeChange(oNode)) {
631
+ oNode = oNode.nextSibling;
632
+ offset = 0;
633
+ }
634
+
635
+ this.selection.setRange(oNode, offset, oNode, offset);
636
+ }
637
+ }
638
+
639
+ if (!result) {
640
+ this.history.push(true);
641
+ result = oNode;
642
+ }
643
+ }
644
+
645
+ return result;
646
+ },
647
+
648
+ /**
649
+ * @description Delete the selected range.
650
+ * Returns {container: "the last element after deletion", offset: "offset", prevContainer: "previousElementSibling Of the deleted area"}
651
+ * @returns {Object}
652
+ */
653
+ remove() {
654
+ this.selection._resetRangeToTextNode();
655
+
656
+ const range = this.selection.getRange();
657
+ const isStartEdge = range.startOffset === 0;
658
+ const isEndEdge = domUtils.isEdgePoint(range.endContainer, range.endOffset, 'end');
659
+ let prevContainer = null;
660
+ let startPrevEl = null;
661
+ let endNextEl = null;
662
+ if (isStartEdge) {
663
+ startPrevEl = this.format.getLine(range.startContainer);
664
+ prevContainer = startPrevEl ? startPrevEl.previousElementSibling : null;
665
+ startPrevEl = startPrevEl ? prevContainer : startPrevEl;
666
+ }
667
+ if (isEndEdge) {
668
+ endNextEl = this.format.getLine(range.endContainer);
669
+ endNextEl = endNextEl ? endNextEl.nextElementSibling : endNextEl;
670
+ }
671
+
672
+ let container,
673
+ offset = 0;
674
+ let startCon = range.startContainer;
675
+ let endCon = range.endContainer;
676
+ let startOff = range.startOffset;
677
+ let endOff = range.endOffset;
678
+ const commonCon = range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer;
679
+ if (commonCon === startCon && commonCon === endCon) {
680
+ if (this.component.isBasic(commonCon)) {
681
+ const compInfo = this.component.get(commonCon);
682
+ const compContainer = compInfo.container;
683
+ const parent = compContainer.parentNode;
684
+
685
+ const next = compContainer.nextSibling || compContainer.previousSibling;
686
+ const nextOffset = next === compContainer.previousSibling ? next?.textContent?.length || 1 : 0;
687
+ const parentNext = parent.nextElementSibling || parent.previousElementSibling;
688
+ const parentNextOffset = parentNext === parent.previousElementSibling ? parentNext?.textContent?.length || 1 : 0;
689
+
690
+ domUtils.removeItem(compContainer);
691
+
692
+ if (this.format.isLine(parent)) {
693
+ if (parent.childNodes.length === 0) {
694
+ domUtils.removeItem(parent);
695
+ return {
696
+ container: parentNext,
697
+ offset: parentNextOffset
698
+ };
699
+ } else {
700
+ return {
701
+ container: next,
702
+ offset: nextOffset
703
+ };
704
+ }
705
+ } else {
706
+ return {
707
+ container: parentNext,
708
+ offset: parentNextOffset
709
+ };
710
+ }
711
+ } else {
712
+ 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'));
715
+ const line = this.format.getLine(commonCon);
716
+ domUtils.removeItem(commonCon);
717
+
718
+ let rEl = nextEl || prevEl;
719
+ let rOffset = nextEl ? 0 : rEl?.nodeType === 3 ? rEl.textContent.length : 1;
720
+
721
+ const npEl = this.format.getLine(rEl) || this.component.get(rEl);
722
+ if (line !== npEl) {
723
+ rEl = npEl;
724
+ rOffset = rOffset === 0 ? 0 : 1;
725
+ }
726
+
727
+ if (domUtils.isZeroWith(line)) {
728
+ domUtils.removeItem(line);
729
+ }
730
+
731
+ return {
732
+ container: rEl,
733
+ offset: rOffset
734
+ };
735
+ }
736
+
737
+ startCon = commonCon.children[startOff];
738
+ endCon = commonCon.children[endOff];
739
+ startOff = endOff = 0;
740
+ }
741
+ }
742
+
743
+ if (!startCon || !endCon)
744
+ return {
745
+ container: commonCon,
746
+ offset: 0
747
+ };
748
+
749
+ if (startCon === endCon && range.collapsed) {
750
+ if (domUtils.isZeroWith(startCon.textContent?.substr(startOff))) {
751
+ return {
752
+ container: startCon,
753
+ offset: startOff,
754
+ prevContainer: startCon && startCon.parentNode ? startCon : null
755
+ };
756
+ }
757
+ }
758
+
759
+ let beforeNode = null;
760
+ let afterNode = null;
761
+
762
+ const childNodes = domUtils.getListChildNodes(commonCon, null);
763
+ let startIndex = domUtils.getArrayIndex(childNodes, startCon);
764
+ let endIndex = domUtils.getArrayIndex(childNodes, endCon);
765
+
766
+ if (childNodes.length > 0 && startIndex > -1 && endIndex > -1) {
767
+ for (let i = startIndex + 1, startNode = startCon; i >= 0; i--) {
768
+ if (childNodes[i] === startNode.parentNode && childNodes[i].firstChild === startNode && startOff === 0) {
769
+ startIndex = i;
770
+ startNode = startNode.parentNode;
771
+ }
772
+ }
773
+
774
+ for (let i = endIndex - 1, endNode = endCon; i > startIndex; i--) {
775
+ if (childNodes[i] === endNode.parentNode && childNodes[i].nodeType === 1) {
776
+ childNodes.splice(i, 1);
777
+ endNode = endNode.parentNode;
778
+ --endIndex;
779
+ }
780
+ }
781
+ } else {
782
+ if (childNodes.length === 0) {
783
+ if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) || domUtils.isWysiwygFrame(commonCon) || domUtils.isBreak(commonCon) || domUtils.isMedia(commonCon)) {
784
+ return {
785
+ container: commonCon,
786
+ offset: 0
787
+ };
788
+ } else if (commonCon.nodeType === 3) {
789
+ return {
790
+ container: commonCon,
791
+ offset: endOff
792
+ };
793
+ }
794
+ childNodes.push(commonCon);
795
+ startCon = endCon = commonCon;
796
+ } else {
797
+ startCon = endCon = childNodes[0];
798
+ if (domUtils.isBreak(startCon) || domUtils.isZeroWith(startCon)) {
799
+ return {
800
+ container: domUtils.isMedia(commonCon) ? commonCon : startCon,
801
+ offset: 0
802
+ };
803
+ }
804
+ }
805
+
806
+ startIndex = endIndex = 0;
807
+ }
808
+
809
+ for (let i = startIndex; i <= endIndex; i++) {
810
+ const item = childNodes[i];
811
+
812
+ if (item.length === 0 || (item.nodeType === 3 && item.data === undefined)) {
813
+ this._nodeRemoveListItem(item);
814
+ continue;
815
+ }
816
+
817
+ if (item === startCon) {
818
+ if (startCon.nodeType === 1) {
819
+ if (this.component.is(startCon)) continue;
820
+ else beforeNode = domUtils.createTextNode(startCon.textContent);
821
+ } else {
822
+ if (item === endCon) {
823
+ beforeNode = domUtils.createTextNode(startCon.substringData(0, startOff) + endCon.substringData(endOff, endCon.length - endOff));
824
+ offset = startOff;
825
+ } else {
826
+ beforeNode = domUtils.createTextNode(startCon.substringData(0, startOff));
827
+ }
828
+ }
829
+
830
+ if (beforeNode.length > 0) {
831
+ startCon.data = beforeNode.data;
832
+ } else {
833
+ this._nodeRemoveListItem(startCon);
834
+ }
835
+
836
+ if (item === endCon) break;
837
+ continue;
838
+ }
839
+
840
+ if (item === endCon) {
841
+ if (endCon.nodeType === 1) {
842
+ if (this.component.is(endCon)) continue;
843
+ else afterNode = domUtils.createTextNode(endCon.textContent);
844
+ } else {
845
+ afterNode = domUtils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
846
+ }
847
+
848
+ if (afterNode.length > 0) {
849
+ endCon.data = afterNode.data;
850
+ } else {
851
+ this._nodeRemoveListItem(endCon);
852
+ }
853
+
854
+ continue;
855
+ }
856
+
857
+ this._nodeRemoveListItem(item);
858
+ }
859
+
860
+ const endUl = domUtils.getParentElement(endCon, 'ul');
861
+ const startLi = domUtils.getParentElement(startCon, 'li');
862
+ if (endUl && startLi && startLi.contains(endUl)) {
863
+ container = endUl.previousSibling;
864
+ offset = container.textContent.length;
865
+ } else {
866
+ container = endCon && endCon.parentNode ? endCon : startCon && startCon.parentNode ? startCon : range.endContainer || range.startContainer;
867
+ offset = !isStartEdge && !isEndEdge ? offset : isEndEdge ? container.textContent.length : 0;
868
+ }
869
+
870
+ if (!this.format.getLine(container) && !(startCon && startCon.parentNode)) {
871
+ if (endNextEl) {
872
+ container = endNextEl;
873
+ offset = 0;
874
+ } else if (startPrevEl) {
875
+ container = startPrevEl;
876
+ offset = 1;
877
+ }
878
+ }
879
+
880
+ if (!domUtils.isWysiwygFrame(container) && container.childNodes.length === 0) {
881
+ const rc = this.nodeTransform.removeAllParents(container, null, null);
882
+ if (rc) container = rc.sc || rc.ec || this.editor.frameContext.get('wysiwyg');
883
+ }
884
+
885
+ // set range
886
+ this.selection.setRange(container, offset, container, offset);
887
+
888
+ return {
889
+ container: container,
890
+ offset: offset,
891
+ prevContainer: prevContainer
892
+ };
893
+ },
894
+
895
+ /**
896
+ * @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>).
898
+ * 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>}
902
+ */
903
+ get(withFrame, includeFullPage, rootKey) {
904
+ if (!rootKey) rootKey = [this.status.rootKey];
905
+ else if (!Array.isArray(rootKey)) rootKey = [rootKey];
906
+
907
+ const prevrootKey = this.status.rootKey;
908
+ const resultValue = {};
909
+ for (let i = 0, len = rootKey.length, r; i < len; i++) {
910
+ this.editor.changeFrameContext(rootKey[i]);
911
+
912
+ 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'));
915
+
916
+ for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
917
+ editableEls[j].removeAttribute('contenteditable');
918
+ }
919
+
920
+ const content = this.clean(renderHTML.innerHTML, false, null, null);
921
+ if (this.editor.frameOptions.get('iframe_fullPage')) {
922
+ 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>';
925
+ } else {
926
+ r = content;
927
+ }
928
+ } else {
929
+ r = withFrame ? '<div class="sun-editor-editable' + (this.options.get('_rtl') ? ' se-rtl' : '') + '">' + content + '</div>' : renderHTML.innerHTML;
930
+ }
931
+
932
+ resultValue[rootKey[i]] = r;
933
+ }
934
+
935
+ this.editor.changeFrameContext(prevrootKey);
936
+ return rootKey.length > 1 ? resultValue : resultValue[rootKey[0]];
937
+ },
938
+
939
+ /**
940
+ * @description Sets the HTML string
941
+ * @param {string|undefined} html HTML string
942
+ * @param {number|Array.<number>|undefined} rootKey Root index
943
+ */
944
+ set(html, rootKey) {
945
+ this.selection.removeRange();
946
+ const convertValue = html === null || html === undefined ? '' : this.clean(html, true, null, null);
947
+
948
+ if (!rootKey) rootKey = [this.status.rootKey];
949
+ else if (!Array.isArray(rootKey)) rootKey = [rootKey];
950
+
951
+ for (let i = 0; i < rootKey.length; i++) {
952
+ this.editor.changeFrameContext(rootKey[i]);
953
+
954
+ if (!this.editor.frameContext.get('isCodeView')) {
955
+ this.editor.frameContext.get('wysiwyg').innerHTML = convertValue;
956
+ this.editor._resetComponents();
957
+ this.history.push(false, rootKey[i]);
958
+ } else {
959
+ const value = this._convertToCode(convertValue, false);
960
+ this.viewer._setCodeView(value);
961
+ }
962
+ }
963
+ },
964
+
965
+ /**
966
+ * @description Add content to the end of content.
967
+ * @param {string} content Content to Input
968
+ * @param {number|Array.<number>|undefined} rootKey Root index
969
+ */
970
+ add(content, rootKey) {
971
+ if (!rootKey) rootKey = [this.status.rootKey];
972
+ else if (!Array.isArray(rootKey)) rootKey = [rootKey];
973
+
974
+ for (let i = 0; i < rootKey.length; i++) {
975
+ this.editor.changeFrameContext(rootKey[i]);
976
+ const convertValue = this.clean(content, true, null, null);
977
+
978
+ if (!this.editor.frameContext.get('isCodeView')) {
979
+ const temp = domUtils.createElement('DIV', null, convertValue);
980
+ const children = temp.children;
981
+ const len = children.length;
982
+ for (let j = 0; j < len; j++) {
983
+ if (!children[j]) continue;
984
+ this.editor.frameContext.get('wysiwyg').appendChild(children[j]);
985
+ }
986
+ this.history.push(false, rootKey[i]);
987
+ this.selection.scrollTo(children[len - 1]);
988
+ } else {
989
+ this.viewer._setCodeView(this.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false));
990
+ }
991
+ }
992
+ },
993
+
994
+ /**
995
+ * @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
998
+ */
999
+ setFullPage(ctx, rootKey) {
1000
+ if (!this.editor.frameOptions.get('iframe')) return false;
1001
+
1002
+ if (!rootKey) rootKey = [this.status.rootKey];
1003
+ else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1004
+
1005
+ for (let i = 0; i < rootKey.length; i++) {
1006
+ this.editor.changeFrameContext(rootKey[i]);
1007
+ 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);
1009
+ this.editor._resetComponents();
1010
+ }
1011
+ },
1012
+
1013
+ /**
1014
+ * @description HTML code compression
1015
+ * @param {string} html HTML string
1016
+ * @returns {string} HTML string
1017
+ */
1018
+ compress(html) {
1019
+ return html.replace(/\n/g, '').replace(/(>)(?:\s+)(<)/g, '$1$2');
1020
+ },
1021
+
1022
+ /**
1023
+ * @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.
1026
+ * @returns {string}
1027
+ */
1028
+ _convertToCode(html, comp) {
1029
+ let returnHTML = '';
1030
+ const wRegExp = RegExp;
1031
+ const brReg = new wRegExp('^(BLOCKQUOTE|PRE|TABLE|THEAD|TBODY|TR|TH|TD|OL|UL|IMG|IFRAME|VIDEO|AUDIO|FIGURE|FIGCAPTION|HR|BR|CANVAS|SELECT)$', 'i');
1032
+ const wDoc = typeof html === 'string' ? this._d.createRange().createContextualFragment(html) : html;
1033
+ const isFormat = (current) => {
1034
+ return this.format.isLine(current) || this.component.is(current);
1035
+ };
1036
+ const brChar = comp ? '' : '\n';
1037
+
1038
+ let indentSize = comp ? 0 : this.status.codeIndentSize * 1;
1039
+ indentSize = indentSize > 0 ? new Array(indentSize + 1).join(' ') : '';
1040
+
1041
+ (function recursionFunc(element, indent) {
1042
+ const children = element.childNodes;
1043
+ const elementRegTest = brReg.test(element.nodeName);
1044
+ const elementIndent = elementRegTest ? indent : '';
1045
+
1046
+ for (let i = 0, len = children.length, node, br, lineBR, nodeRegTest, tag, tagIndent; i < len; i++) {
1047
+ node = children[i];
1048
+ nodeRegTest = brReg.test(node.nodeName);
1049
+ br = nodeRegTest ? brChar : '';
1050
+ lineBR = isFormat(node) && !elementRegTest && !/^(TH|TD)$/i.test(element.nodeName) ? brChar : '';
1051
+
1052
+ if (node.nodeType === 8) {
1053
+ returnHTML += '\n<!-- ' + node.textContent.trim() + ' -->' + br;
1054
+ continue;
1055
+ }
1056
+ if (node.nodeType === 3) {
1057
+ if (!domUtils.isList(node.parentElement)) returnHTML += converter.htmlToEntity(/^\n+$/.test(node.data) ? '' : node.data);
1058
+ continue;
1059
+ }
1060
+ 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;
1062
+ continue;
1063
+ }
1064
+
1065
+ if (!node.outerHTML) {
1066
+ returnHTML += new XMLSerializer().serializeToString(node);
1067
+ } else {
1068
+ tag = node.nodeName.toLowerCase();
1069
+ 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 : '');
1073
+ }
1074
+ }
1075
+ })(wDoc, '');
1076
+
1077
+ return returnHTML.trim() + brChar;
1078
+ },
1079
+
1080
+ _nodeRemoveListItem(item) {
1081
+ const line = this.format.getLine(item, null);
1082
+ domUtils.removeItem(item);
1083
+
1084
+ if (!domUtils.isListCell(line)) return;
1085
+
1086
+ this.nodeTransform.removeAllParents(line, null, null);
1087
+
1088
+ if (domUtils.isList(line?.firstChild)) {
1089
+ line.insertBefore(domUtils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
1090
+ }
1091
+ },
1092
+
1093
+ /**
1094
+ * @description Recursive function when used to place a node in "BrLine" in "html.insertNode"
1095
+ * @param {Node} oNode Node to be inserted
1096
+ * @returns {Node} "oNode"
1097
+ * @private
1098
+ */
1099
+ _setIntoFreeFormat(oNode) {
1100
+ const parentNode = oNode.parentNode;
1101
+ let oNodeChildren, lastONode;
1102
+
1103
+ while (this.format.isLine(oNode) || this.format.isBlock(oNode)) {
1104
+ oNodeChildren = oNode.childNodes;
1105
+ lastONode = null;
1106
+
1107
+ while (oNodeChildren[0]) {
1108
+ lastONode = oNodeChildren[0];
1109
+ if (this.format.isLine(lastONode) || this.format.isBlock(lastONode)) {
1110
+ this._setIntoFreeFormat(lastONode);
1111
+ if (!oNode.parentNode) break;
1112
+ oNodeChildren = oNode.childNodes;
1113
+ continue;
1114
+ }
1115
+
1116
+ parentNode.insertBefore(lastONode, oNode);
1117
+ }
1118
+
1119
+ if (oNode.childNodes.length === 0) domUtils.removeItem(oNode);
1120
+ oNode = domUtils.createElement('BR');
1121
+ parentNode.insertBefore(oNode, lastONode.nextSibling);
1122
+ }
1123
+
1124
+ return oNode;
1125
+ },
1126
+
1127
+ /**
1128
+ * @description Returns HTML string according to tag type and configurati isExcludeFormat.
1129
+ * @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
1132
+ */
1133
+ _makeLine(node, requireFormat) {
1134
+ const defaultLine = this.options.get('defaultLine');
1135
+ // element
1136
+ if (node.nodeType === 1) {
1137
+ if (this.__disallowedTagNameRegExp.test(node.nodeName)) return '';
1138
+ if (domUtils.isExcludeFormat(node)) return node.outerHTML;
1139
+
1140
+ const ch =
1141
+ domUtils.getListChildNodes(node, (current) => {
1142
+ return domUtils.isSpanWithoutAttr(current) && !domUtils.getParentElement(current, domUtils.isExcludeFormat);
1143
+ }) || [];
1144
+ for (let i = ch.length - 1; i >= 0; i--) {
1145
+ ch[i].outerHTML = ch[i].innerHTML;
1146
+ }
1147
+
1148
+ if (
1149
+ !requireFormat ||
1150
+ this.format.isLine(node) ||
1151
+ this.format.isBlock(node) ||
1152
+ this.component.is(node) ||
1153
+ domUtils.isMedia(node) ||
1154
+ domUtils.isFigure(node) ||
1155
+ (domUtils.isAnchor(node) && domUtils.isMedia(node.firstElementChild))
1156
+ ) {
1157
+ return domUtils.isSpanWithoutAttr(node) ? node.innerHTML : node.outerHTML;
1158
+ } else {
1159
+ return '<' + defaultLine + '>' + (domUtils.isSpanWithoutAttr(node) ? node.innerHTML : node.outerHTML) + '</' + defaultLine + '>';
1160
+ }
1161
+ }
1162
+ // text
1163
+ if (node.nodeType === 3) {
1164
+ if (!requireFormat) return converter.htmlToEntity(node.textContent);
1165
+ const textArray = node.textContent.split(/\n/g);
1166
+ let html = '';
1167
+ for (let i = 0, tLen = textArray.length, text; i < tLen; i++) {
1168
+ text = textArray[i].trim();
1169
+ if (text.length > 0) html += '<' + defaultLine + '>' + converter.htmlToEntity(text) + '</' + defaultLine + '>';
1170
+ }
1171
+ return html;
1172
+ }
1173
+ // comments
1174
+ if (node.nodeType === 8 && this._allowHTMLComment) {
1175
+ return '<!--' + node.textContent.trim() + '-->';
1176
+ }
1177
+
1178
+ return '';
1179
+ },
1180
+
1181
+ /**
1182
+ * @description Fix tags that do not fit the editor format.
1183
+ * @param {Element} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
1184
+ * @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist
1185
+ * @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist
1186
+ * @private
1187
+ */
1188
+ _consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter) {
1189
+ const removeTags = [],
1190
+ emptyTags = [],
1191
+ wrongList = [],
1192
+ withoutFormatCells = [];
1193
+
1194
+ // wrong position
1195
+ const wrongTags = domUtils.getListChildNodes(documentFragment, (current) => {
1196
+ if (formatFilter && current.nodeType !== 1) {
1197
+ if (domUtils.isList(current.parentElement)) removeTags.push(current);
1198
+ return false;
1199
+ }
1200
+
1201
+ // tag filter
1202
+ if (tagFilter) {
1203
+ // white list
1204
+ if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && domUtils.isExcludeFormat(current))) {
1205
+ removeTags.push(current);
1206
+ return false;
1207
+ }
1208
+ }
1209
+
1210
+ const nrtag = !domUtils.getParentElement(current, domUtils.isExcludeFormat);
1211
+
1212
+ // formatFilter
1213
+ if (formatFilter) {
1214
+ // empty tags
1215
+ if (
1216
+ !domUtils.isTableElements(current) &&
1217
+ !domUtils.isListCell(current) &&
1218
+ !domUtils.isAnchor(current) &&
1219
+ (this.format.isLine(current) || this.format.isBlock(current) || this.format.isTextStyleNode(current)) &&
1220
+ current.childNodes.length === 0 &&
1221
+ nrtag
1222
+ ) {
1223
+ emptyTags.push(current);
1224
+ return false;
1225
+ }
1226
+
1227
+ // wrong list
1228
+ if (domUtils.isList(current.parentNode) && !domUtils.isList(current) && !domUtils.isListCell(current)) {
1229
+ wrongList.push(current);
1230
+ return false;
1231
+ }
1232
+
1233
+ // table cells
1234
+ if (domUtils.isTableCell(current)) {
1235
+ const fel = current.firstElementChild;
1236
+ if (!this.format.isLine(fel) && !this.format.isBlock(fel) && !this.component.is(fel)) {
1237
+ withoutFormatCells.push(current);
1238
+ return false;
1239
+ }
1240
+ }
1241
+ }
1242
+
1243
+ // class filter
1244
+ if (classFilter) {
1245
+ if (nrtag && current.className) {
1246
+ const className = new Array(current.classList).map(this._isAllowedClassName).join(' ').trim();
1247
+ if (className) current.className = className;
1248
+ else current.removeAttribute('class');
1249
+ }
1250
+ }
1251
+
1252
+ // format filter
1253
+ if (!formatFilter) {
1254
+ return false;
1255
+ }
1256
+
1257
+ const result =
1258
+ current.parentNode !== documentFragment &&
1259
+ 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))));
1262
+
1263
+ return result;
1264
+ });
1265
+
1266
+ for (let i = 0, len = removeTags.length; i < len; i++) {
1267
+ domUtils.removeItem(removeTags[i]);
1268
+ }
1269
+
1270
+ const checkTags = [];
1271
+ for (let i = 0, len = wrongTags.length, t, p; i < len; i++) {
1272
+ t = wrongTags[i];
1273
+ p = t.parentNode;
1274
+ if (!p || !p.parentNode) continue;
1275
+
1276
+ if (domUtils.getParentElement(t, domUtils.isListCell)) {
1277
+ const cellChildren = t.childNodes;
1278
+ for (let j = cellChildren.length - 1; len >= 0; j--) {
1279
+ p.insertBefore(t, cellChildren[j]);
1280
+ }
1281
+ checkTags.push(t);
1282
+ } else {
1283
+ p.parentNode.insertBefore(t, p);
1284
+ checkTags.push(p);
1285
+ }
1286
+ }
1287
+
1288
+ for (let i = 0, len = checkTags.length, t; i < len; i++) {
1289
+ t = checkTags[i];
1290
+ if (domUtils.isZeroWith(t.textContent.trim())) {
1291
+ domUtils.removeItem(t);
1292
+ }
1293
+ }
1294
+
1295
+ for (let i = 0, len = emptyTags.length; i < len; i++) {
1296
+ domUtils.removeItem(emptyTags[i]);
1297
+ }
1298
+
1299
+ for (let i = 0, len = wrongList.length, t, tp, children, p; i < len; i++) {
1300
+ t = wrongList[i];
1301
+ p = t.parentNode;
1302
+ if (!p) continue;
1303
+
1304
+ tp = domUtils.createElement('LI');
1305
+
1306
+ if (this.format.isLine(t)) {
1307
+ children = t.childNodes;
1308
+ while (children[0]) {
1309
+ tp.appendChild(children[0]);
1310
+ }
1311
+ p.insertBefore(tp, t);
1312
+ domUtils.removeItem(t);
1313
+ } else {
1314
+ t = t.nextSibling;
1315
+ tp.appendChild(wrongList[i]);
1316
+ p.insertBefore(tp, t);
1317
+ }
1318
+ }
1319
+
1320
+ for (let i = 0, len = withoutFormatCells.length, t, f; i < len; i++) {
1321
+ t = withoutFormatCells[i];
1322
+ f = domUtils.createElement('DIV');
1323
+ f.innerHTML = t.textContent.trim().length === 0 && t.children.length === 0 ? '<br>' : t.innerHTML;
1324
+ t.innerHTML = f.outerHTML;
1325
+ }
1326
+ },
1327
+
1328
+ /**
1329
+ * @description Removes attribute values such as style and converts tags that do not conform to the "html5" standard.
1330
+ * @param {string} html HTML string
1331
+ * @returns {string} HTML string
1332
+ * @private
1333
+ */
1334
+ _styleNodeConvertor(html) {
1335
+ if (!this._disallowedStyleNodesRegExp) return html;
1336
+
1337
+ const ec = this.options.get('_defaultStyleTagMap');
1338
+ return html.replace(this._disallowedStyleNodesRegExp, (m, t, n, p) => {
1339
+ return t + (typeof ec[n] === 'string' ? ec[n] : n) + (p ? ' ' + p : '');
1340
+ });
1341
+ },
1342
+
1343
+ /**
1344
+ * @description Determines if formatting is required and returns a domTree
1345
+ * @param {Element} dom documentFragment
1346
+ * @returns {Element}
1347
+ * @private
1348
+ */
1349
+ _editFormat(dom) {
1350
+ let value = '',
1351
+ f;
1352
+ const tempTree = dom.childNodes;
1353
+
1354
+ for (let i = 0, len = tempTree.length, n; i < len; i++) {
1355
+ n = tempTree[i];
1356
+ if (this.__allowedTagNameRegExp.test(n.nodeName)) {
1357
+ value += n.outerHTML;
1358
+ continue;
1359
+ }
1360
+
1361
+ if (n.nodeType === 8) {
1362
+ 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'));
1365
+ f.appendChild(n);
1366
+ i--;
1367
+ len--;
1368
+ } else {
1369
+ if (f) {
1370
+ value += f.outerHTML;
1371
+ f = null;
1372
+ }
1373
+ value += n.outerHTML;
1374
+ }
1375
+ }
1376
+
1377
+ if (f) value += f.outerHTML;
1378
+
1379
+ return this._d.createRange().createContextualFragment(value);
1380
+ },
1381
+
1382
+ _convertListCell(domTree) {
1383
+ let html = '';
1384
+
1385
+ for (let i = 0, len = domTree.length, node; i < len; i++) {
1386
+ node = domTree[i];
1387
+ if (node.nodeType === 1) {
1388
+ if (domUtils.isList(node)) {
1389
+ html += node.innerHTML;
1390
+ } else if (domUtils.isListCell(node)) {
1391
+ html += node.outerHTML;
1392
+ } else if (this.format.isLine(node)) {
1393
+ html += '<li>' + (node.innerHTML.trim() || '<br>') + '</li>';
1394
+ } else if (this.format.isBlock(node) && !domUtils.isTableElements(node)) {
1395
+ html += this._convertListCell(node);
1396
+ } else {
1397
+ html += '<li>' + node.outerHTML + '</li>';
1398
+ }
1399
+ } else {
1400
+ html += '<li>' + (node.textContent || '<br>') + '</li>';
1401
+ }
1402
+ }
1403
+
1404
+ return html;
1405
+ },
1406
+
1407
+ _isFormatData(domTree) {
1408
+ let requireFormat = false;
1409
+
1410
+ for (let i = 0, len = domTree.length, t; i < len; i++) {
1411
+ t = domTree[i];
1412
+ if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !domUtils.isBreak(t) && !this.__disallowedTagNameRegExp.test(t.nodeName)) {
1413
+ requireFormat = true;
1414
+ break;
1415
+ }
1416
+ }
1417
+
1418
+ return requireFormat;
1419
+ },
1420
+
1421
+ _cleanStyle(m, v, name) {
1422
+ let sv = (m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/) || [])[0];
1423
+ if (this._textStyleTags.includes(name) && !sv && (m.match(/<[^\s]+\s(.+)/) || [])[1]) {
1424
+ const size = (m.match(/\ssize="([^"]+)"/i) || [])[1];
1425
+ const face = (m.match(/\sface="([^"]+)"/i) || [])[1];
1426
+ const color = (m.match(/\scolor="([^"]+)"/i) || [])[1];
1427
+ if (size || face || color) {
1428
+ sv = 'style="' + (size ? 'font-size:' + numbers.get(size / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
1429
+ }
1430
+ }
1431
+
1432
+ if (sv) {
1433
+ if (!v) v = [];
1434
+
1435
+ let mv;
1436
+ for (const [key, value] of this._cleanStyleRegExpMap) {
1437
+ if (key.test(name)) {
1438
+ mv = value;
1439
+ break;
1440
+ }
1441
+ }
1442
+ if (!mv) return v;
1443
+
1444
+ const style = sv.replace(/&quot;/g, '').match(mv);
1445
+ if (!style) return v;
1446
+
1447
+ const allowedStyle = [];
1448
+ for (let i = 0, len = style.length, r; i < len; i++) {
1449
+ r = style[i].match(/([a-zA-Z0-9-]+)(:)([^"']+)/);
1450
+ if (r && !/inherit|initial|revert|unset/i.test(r[3])) {
1451
+ const k = env.kebabToCamelCase(r[1].trim());
1452
+ const cs = this.editor.frameContext.get('wwComputedStyle')[k].replace(/"/g, '');
1453
+ const c = r[3].trim();
1454
+ switch (k) {
1455
+ case 'fontFamily':
1456
+ if (!this.plugins.font || !this.plugins.font.fontArray.includes(c)) continue;
1457
+ break;
1458
+ case 'fontSize':
1459
+ if (!this.plugins.fontSize) continue;
1460
+ if (!this.fontSizeUnitRegExp.test(r[0])) {
1461
+ r[0] = r[0].replace((r[0].match(/:\s*([^;]+)/) || [])[1], converter.fontSize.bind(null, this.options.get('fontSizeUnits')[0]));
1462
+ }
1463
+ break;
1464
+ case 'color':
1465
+ if (!this.plugins.fontColor || /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i.test(c)) continue;
1466
+ break;
1467
+ case 'backgroundColor':
1468
+ if (!this.plugins.backgroundColor || /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i.test(c)) continue;
1469
+ break;
1470
+ }
1471
+
1472
+ if (cs !== c) {
1473
+ allowedStyle.push(r[0]);
1474
+ }
1475
+ }
1476
+ }
1477
+ if (allowedStyle.length > 0) v.push('style="' + allowedStyle.join(';') + '"');
1478
+ }
1479
+
1480
+ return v;
1481
+ },
1482
+
1483
+ /**
1484
+ * @description Delete disallowed tags
1485
+ * @param {string} html HTML string
1486
+ * @returns {string}
1487
+ * @private
1488
+ */
1489
+ _deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
1490
+ html = html.replace(this.__disallowedTagsRegExp, '');
1491
+ if (whitelistRegExp.test('<font>')) {
1492
+ html = html.replace(/(<\/?)font(\s?)/gi, '$1span$2');
1493
+ }
1494
+
1495
+ return html.replace(whitelistRegExp, '').replace(blacklistRegExp, '');
1496
+ },
1497
+
1498
+ _checkDuplicateNode(oNode, parentNode) {
1499
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
1500
+ const inst = this;
1501
+ (function recursionFunc(current) {
1502
+ inst._dupleCheck(current, parentNode);
1503
+ const childNodes = current.childNodes;
1504
+ for (let i = 0, len = childNodes.length; i < len; i++) {
1505
+ recursionFunc(childNodes[i]);
1506
+ }
1507
+ })(oNode);
1508
+ },
1509
+
1510
+ _dupleCheck(oNode, parentNode) {
1511
+ if (!this.format.isTextStyleNode(oNode)) return;
1512
+
1513
+ const oStyles = (oNode.style.cssText.match(/[^;]+;/g) || []).map(function (v) {
1514
+ return v.trim();
1515
+ });
1516
+ const nodeName = oNode.nodeName;
1517
+ if (/^span$/i.test(nodeName) && oStyles.length === 0) return oNode;
1518
+
1519
+ const inst = this.format;
1520
+ let duple = false;
1521
+ (function recursionFunc(ancestor) {
1522
+ if (domUtils.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
1523
+ if (ancestor.nodeName === nodeName) {
1524
+ duple = true;
1525
+ const styles = ancestor.style.cssText.match(/[^;]+;/g) || [];
1526
+ for (let i = 0, len = styles.length, j; i < len; i++) {
1527
+ if ((j = oStyles.indexOf(styles[i].trim())) > -1) {
1528
+ oStyles.splice(j, 1);
1529
+ }
1530
+ }
1531
+ for (let i = 0, len = ancestor.classList.length; i < len; i++) {
1532
+ oNode.classList.remove(ancestor.classList[i]);
1533
+ }
1534
+ }
1535
+
1536
+ recursionFunc(ancestor.parentElement);
1537
+ })(parentNode);
1538
+
1539
+ if (duple) {
1540
+ if (!(oNode.style.cssText = oStyles.join(' '))) {
1541
+ oNode.setAttribute('style', '');
1542
+ oNode.removeAttribute('style');
1543
+ }
1544
+ if (!oNode.attributes.length) {
1545
+ oNode.setAttribute('data-duple', 'true');
1546
+ }
1547
+ }
1548
+
1549
+ return oNode;
1550
+ },
1551
+
1552
+ constructor: HTML
1553
+ };
1554
+
1555
+ /**
1556
+ * @description Tag and tag attribute check RegExp function.
1557
+ * @param {string} m RegExp value
1558
+ * @param {string} t RegExp value
1559
+ * @returns {string}
1560
+ * @private
1561
+ */
1562
+ function CleanElements(attrFilter, styleFilter, m, t) {
1563
+ if (/^<[a-z0-9]+:[a-z0-9]+/i.test(m)) return m;
1564
+
1565
+ let v = null;
1566
+ const tagName = t.match(/(?!<)[a-zA-Z0-9-]+/)[0].toLowerCase();
1567
+
1568
+ if (attrFilter) {
1569
+ // blacklist
1570
+ const bAttr = this._attributeBlacklist[tagName];
1571
+ m = m.replace(/\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/gi, '');
1572
+ if (bAttr) m = m.replace(bAttr, '');
1573
+ else m = m.replace(this._attributeBlacklistRegExp, '');
1574
+
1575
+ // whitelist
1576
+ const wAttr = this._attributeWhitelist[tagName];
1577
+ if (wAttr) v = m.match(wAttr);
1578
+ else v = m.match(this._attributeWhitelistRegExp);
1579
+ }
1580
+
1581
+ if (!styleFilter) return m;
1582
+
1583
+ // attribute
1584
+ if (tagName === 'a') {
1585
+ const sv = m.match(/(?:(?:id|name)\s*=\s*(?:"|')[^"']*(?:"|'))/g);
1586
+ if (sv) {
1587
+ if (!v) v = [];
1588
+ v.push(sv[0]);
1589
+ }
1590
+ } else if (!v || !/style=/i.test(v.toString())) {
1591
+ if (this._textStyleTags.includes(tagName)) {
1592
+ v = this._cleanStyle(m, v, tagName);
1593
+ } else if (this.format.isLine(tagName)) {
1594
+ v = this._cleanStyle(m, v, 'line');
1595
+ } else if (this._cleanStyleTagKeyRegExp.test(tagName)) {
1596
+ v = this._cleanStyle(m, v, tagName);
1597
+ }
1598
+ }
1599
+
1600
+ // figure
1601
+ if (domUtils.isMedia(tagName) || domUtils.isFigure(tagName)) {
1602
+ const sv = m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/);
1603
+ if (!v) v = [];
1604
+ if (sv) v.push(sv[0]);
1605
+ }
1606
+
1607
+ if (v) {
1608
+ for (let i = 0, len = v.length, a; i < len; i++) {
1609
+ a = /^(?:href|src)\s*=\s*('|"|\s)*javascript\s*:/i.test(v[i].trim()) ? '' : v[i];
1610
+ t += (/^\s/.test(a) ? '' : ' ') + a;
1611
+ }
1612
+ }
1613
+
1614
+ return t;
1615
+ }
1616
+
1617
+ function GetRegList(str, str2) {
1618
+ return !str ? '^' : str === '*' ? '[a-z-]+' : !str2 ? str : str + '|' + str2;
1619
+ }
1620
+
1621
+ export default HTML;