suneditor 2.46.2 → 3.0.0-alpha.10

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