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,1177 @@
1
+ import { _d, _w } from './env';
2
+ import { onlyZeroWidthRegExp, zeroWidthRegExp } from './unicode';
3
+
4
+ /**
5
+ * @description A method that checks If the text is blank or to see if it contains 'ZERO WIDTH SPACE' or empty (unicode.zeroWidthSpace)
6
+ * @param {string|Node} text String value or Node
7
+ * @returns {boolean}
8
+ */
9
+ export function isZeroWith(text) {
10
+ if (text === null || text === undefined) return false;
11
+ if (typeof text !== 'string') text = text.textContent;
12
+ return text === '' || onlyZeroWidthRegExp.test(text);
13
+ }
14
+
15
+ /**
16
+ * @description Create Element node
17
+ * @param {string} elementName Element name
18
+ * @param {Object.<string, string>|null|undefined} attributes The attributes of the tag. {style: 'font-size:12px;..', class: 'el_class',..}
19
+ * @param {string|Node|null|undefined} inner A innerHTML string or inner node.
20
+ * @returns {Element}
21
+ */
22
+ export function createElement(elementName, attributes, inner) {
23
+ const el = _d.createElement(elementName);
24
+
25
+ if (attributes) {
26
+ for (const key in attributes) {
27
+ if (attributes[key] !== undefined && attributes[key] !== null) el.setAttribute(key, attributes[key]);
28
+ }
29
+ }
30
+
31
+ if (inner) {
32
+ if (typeof inner === 'string') {
33
+ el.innerHTML = inner;
34
+ } else if (typeof inner === 'object') {
35
+ el.appendChild(inner);
36
+ }
37
+ }
38
+
39
+ return el;
40
+ }
41
+
42
+ /**
43
+ * @description Create text node
44
+ * @param {string} text text content
45
+ * @returns {Text}
46
+ */
47
+ export function createTextNode(text) {
48
+ return _d.createTextNode(text || '');
49
+ }
50
+
51
+ /**
52
+ * @description Get the argument iframe's document object if use the "iframe" or "fullPage" options
53
+ * @param {Element} iframe Iframe element (this.editor.frameContext.get('wysiwygFrame'))
54
+ * @returns {Document}
55
+ */
56
+ export function getIframeDocument(iframe) {
57
+ let wDocument = iframe.contentWindow || iframe.contentDocument;
58
+ if (wDocument.document) wDocument = wDocument.document;
59
+ return wDocument;
60
+ }
61
+
62
+ /**
63
+ * @description Get attributes of argument element to string ('class="---" name="---" ')
64
+ * @param {Element} element Element object
65
+ * @param {Array.<string>|null} exceptAttrs Array of attribute names to exclude from the result
66
+ * @returns {string}
67
+ */
68
+ export function getAttributesToString(element, exceptAttrs) {
69
+ if (!element.attributes) return '';
70
+
71
+ const attrs = element.attributes;
72
+ let attrString = '';
73
+
74
+ for (let i = 0, len = attrs.length; i < len; i++) {
75
+ if (exceptAttrs?.includes(attrs[i].name)) continue;
76
+ attrString += attrs[i].name + '="' + attrs[i].value + '" ';
77
+ }
78
+
79
+ return attrString;
80
+ }
81
+
82
+ /**
83
+ * @description Returns the index compared to other sibling nodes.
84
+ * @param {Node} node The Node to find index
85
+ * @returns {number}
86
+ */
87
+ export function getPositionIndex(node) {
88
+ let idx = 0;
89
+ while ((node = node.previousSibling)) {
90
+ idx += 1;
91
+ }
92
+ return idx;
93
+ }
94
+
95
+ /**
96
+ * @description Returns the position of the "node" in the "parentNode" in a numerical array.
97
+ * ex) <p><span>aa</span><span>bb</span></p> : getNodePath(node: "bb", parentNode: "<P>") -> [1, 0]
98
+ * @param {Node} node The Node to find position path
99
+ * @param {Node|null} parentNode Parent node. If null, wysiwyg div area
100
+ * @param {{s: number, e: number}|null} _newOffsets If you send an object of the form "{s: 0, e: 0}", the text nodes that are attached together are merged into one, centered on the "node" argument.
101
+ * "_newOffsets.s" stores the length of the combined characters after "node" and "_newOffsets.e" stores the length of the combined characters before "node".
102
+ * Do not use unless absolutely necessary.
103
+ * @returns {Array.<number>}
104
+ */
105
+ export function getNodePath(node, parentNode, _newOffsets) {
106
+ const path = [];
107
+ let finds = true;
108
+
109
+ getParentElement(node, (el) => {
110
+ if (el === parentNode) finds = false;
111
+ if (finds && !isWysiwygFrame(el)) {
112
+ // merge text nodes
113
+ if (_newOffsets && el.nodeType === 3) {
114
+ let temp = null,
115
+ tempText = null;
116
+ _newOffsets.s = _newOffsets.e = 0;
117
+
118
+ let previous = el.previousSibling;
119
+ while (previous?.nodeType === 3) {
120
+ tempText = previous.textContent.replace(zeroWidthRegExp, '');
121
+ _newOffsets.s += tempText.length;
122
+ el.textContent = tempText + el.textContent;
123
+ temp = previous;
124
+ previous = previous.previousSibling;
125
+ removeItem(temp);
126
+ }
127
+
128
+ let next = el.nextSibling;
129
+ while (next?.nodeType === 3) {
130
+ tempText = next.textContent.replace(zeroWidthRegExp, '');
131
+ _newOffsets.e += tempText.length;
132
+ el.textContent += tempText;
133
+ temp = next;
134
+ next = next.nextSibling;
135
+ removeItem(temp);
136
+ }
137
+ }
138
+
139
+ // index push
140
+ path.push(el);
141
+ }
142
+ return false;
143
+ });
144
+
145
+ return path.map(getPositionIndex).reverse();
146
+ }
147
+
148
+ /**
149
+ * @description Returns the node in the location of the path array obtained from "helper.dom.getNodePath".
150
+ * @param {Array.<number>} offsets Position array, array obtained from "helper.dom.getNodePath"
151
+ * @param {Node} parentNode Base parent element
152
+ * @returns {Node}
153
+ */
154
+ export function getNodeFromPath(offsets, parentNode) {
155
+ let current = parentNode;
156
+ let nodes;
157
+
158
+ for (let i = 0, len = offsets.length; i < len; i++) {
159
+ nodes = current.childNodes;
160
+ if (nodes.length === 0) break;
161
+ if (nodes.length <= offsets[i]) {
162
+ current = nodes[nodes.length - 1];
163
+ } else {
164
+ current = nodes[offsets[i]];
165
+ }
166
+ }
167
+
168
+ return current;
169
+ }
170
+
171
+ /**
172
+ * @description Get all "children" of the argument value element (Without text nodes)
173
+ * @param {Element} element element to get child node
174
+ * @param {Function|null} validation Conditional function
175
+ * @returns {Array.<Element>}
176
+ */
177
+ export function getListChildren(element, validation) {
178
+ const children = [];
179
+ if (!element || !element.children || element.children.length === 0) return children;
180
+
181
+ validation =
182
+ validation ||
183
+ function () {
184
+ return true;
185
+ };
186
+
187
+ (function recursionFunc(current) {
188
+ if (element !== current && validation(current)) {
189
+ children.push(current);
190
+ }
191
+
192
+ if (current.children) {
193
+ for (let i = 0, len = current.children.length; i < len; i++) {
194
+ recursionFunc(current.children[i]);
195
+ }
196
+ }
197
+ })(element);
198
+
199
+ return children;
200
+ }
201
+
202
+ /**
203
+ * @description Get all "childNodes" of the argument value element (Include text nodes)
204
+ * @param {Node} element element to get child node
205
+ * @param {Function|null} validation Conditional function
206
+ * @returns {Array.<Node>}
207
+ */
208
+ export function getListChildNodes(element, validation) {
209
+ const children = [];
210
+ if (!element || element.childNodes.length === 0) return children;
211
+
212
+ validation =
213
+ validation ||
214
+ function () {
215
+ return true;
216
+ };
217
+
218
+ (function recursionFunc(current) {
219
+ if (element !== current && validation(current)) {
220
+ children.push(current);
221
+ }
222
+
223
+ for (let i = 0, len = current.childNodes.length; i < len; i++) {
224
+ recursionFunc(current.childNodes[i]);
225
+ }
226
+ })(element);
227
+
228
+ return children;
229
+ }
230
+
231
+ /**
232
+ * @description Returns the number of parents nodes.
233
+ * "0" when the parent node is the WYSIWYG area.
234
+ * '-1' when the element argument is the WYSIWYG area.
235
+ * @param {Node} node The element to check
236
+ * @returns {number}
237
+ */
238
+ export function getNodeDepth(node) {
239
+ if (!node || isWysiwygFrame(node)) return -1;
240
+
241
+ let depth = 0;
242
+ node = node.parentNode;
243
+
244
+ while (node && !isWysiwygFrame(node)) {
245
+ depth += 1;
246
+ node = node.parentNode;
247
+ }
248
+
249
+ return depth;
250
+ }
251
+
252
+ /**
253
+ * @description Sort a node array by depth of element.
254
+ * @param {Array.<Node>} array Node array
255
+ * @param {boolean} des true: descending order / false: ascending order
256
+ */
257
+ export function sortNodeByDepth(array, des) {
258
+ const t = !des ? -1 : 1;
259
+ const f = t * -1;
260
+
261
+ array.sort(function (a, b) {
262
+ if (!isListCell(a) || !isListCell(b)) return 0;
263
+ a = getNodeDepth(a);
264
+ b = getNodeDepth(b);
265
+ return a > b ? t : a < b ? f : 0;
266
+ });
267
+ }
268
+
269
+ /**
270
+ * @description Compares two elements to find a common ancestor, and returns the order of the two elements.
271
+ * @param {Node} a Node to compare.
272
+ * @param {Node} b Node to compare.
273
+ * @returns {{ancesstor: Node, a: Node, b: Node, ressult: number}} { ancesstor, a, b, result: (a > b ? 1 : a < b ? -1 : 0) };
274
+ */
275
+ export function compareElements(a, b) {
276
+ let aNode = a,
277
+ bNode = b;
278
+ while (aNode && bNode && aNode.parentNode !== bNode.parentNode) {
279
+ aNode = aNode.parentNode;
280
+ bNode = bNode.parentNode;
281
+ }
282
+
283
+ if (!aNode || !bNode)
284
+ return {
285
+ ancestor: null,
286
+ a: a,
287
+ b: b,
288
+ result: 0
289
+ };
290
+
291
+ const children = aNode.parentNode.childNodes;
292
+ const aIndex = getArrayIndex(children, aNode);
293
+ const bIndex = getArrayIndex(children, bNode);
294
+
295
+ return {
296
+ ancestor: aNode.parentNode,
297
+ a: aNode,
298
+ b: bNode,
299
+ result: aIndex > bIndex ? 1 : aIndex < bIndex ? -1 : 0
300
+ };
301
+ }
302
+
303
+ /**
304
+ * @description Get the parent element of the argument value.
305
+ * A tag that satisfies the query condition is imported.
306
+ * Returns null if not found.
307
+ * @param {Node} element Reference element
308
+ * @param {string|Function|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
309
+ * Not use it like jquery.
310
+ * Only one condition can be entered at a time.
311
+ * @returns {Element|null}
312
+ */
313
+ export function getParentElement(element, query) {
314
+ let check;
315
+
316
+ if (typeof query === 'function') {
317
+ check = query;
318
+ } else if (typeof query === 'object') {
319
+ check = function (current) {
320
+ return current === query;
321
+ };
322
+ } else {
323
+ let attr;
324
+ if (/^\./.test(query)) {
325
+ attr = 'className';
326
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
327
+ } else if (/^#/.test(query)) {
328
+ attr = 'id';
329
+ query = '^' + query.split('#')[1] + '$';
330
+ } else if (/^:/.test(query)) {
331
+ attr = 'name';
332
+ query = '^' + query.split(':')[1] + '$';
333
+ } else {
334
+ attr = 'nodeName';
335
+ query = '^' + query + '$';
336
+ }
337
+
338
+ const regExp = new RegExp(query, 'i');
339
+ check = function (el) {
340
+ return regExp.test(el[attr]);
341
+ };
342
+ }
343
+
344
+ while (element && !check(element)) {
345
+ if (isWysiwygFrame(element)) {
346
+ return null;
347
+ }
348
+ element = element.parentNode;
349
+ }
350
+
351
+ return element;
352
+ }
353
+
354
+ /**
355
+ * @description Gets all ancestors of the argument value.
356
+ * Get all tags that satisfy the query condition.
357
+ * Returned in an array in order.
358
+ * @param {Node} element Reference element
359
+ * @param {string|Function|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
360
+ * Not use it like jquery.
361
+ * Only one condition can be entered at a time.
362
+ * @returns {Element|null}
363
+ */
364
+ export function getParentElements(element, query) {
365
+ let check;
366
+
367
+ if (typeof query === 'function') {
368
+ check = query;
369
+ } else if (typeof query === 'object') {
370
+ check = function (current) {
371
+ return current === query;
372
+ };
373
+ } else {
374
+ let attr;
375
+ if (/^\./.test(query)) {
376
+ attr = 'className';
377
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
378
+ } else if (/^#/.test(query)) {
379
+ attr = 'id';
380
+ query = '^' + query.split('#')[1] + '$';
381
+ } else if (/^:/.test(query)) {
382
+ attr = 'name';
383
+ query = '^' + query.split(':')[1] + '$';
384
+ } else {
385
+ attr = 'nodeName';
386
+ query = '^' + query + '$';
387
+ }
388
+
389
+ const regExp = new RegExp(query, 'i');
390
+ check = function (el) {
391
+ return regExp.test(el[attr]);
392
+ };
393
+ }
394
+
395
+ const elementList = [];
396
+ while (element && !isWysiwygFrame(element)) {
397
+ if (check(element)) {
398
+ elementList.push(element);
399
+ }
400
+ element = element.parentNode;
401
+ }
402
+
403
+ return elementList;
404
+ }
405
+
406
+ /**
407
+ * @description Gets the element with "data-command" attribute among the parent elements.
408
+ * @param {Element} target Target element
409
+ * @returns {Element|null}
410
+ */
411
+ export function getCommandTarget(target) {
412
+ while (!/^(UL|DIV)$/i.test(target.tagName)) {
413
+ if (target.hasAttribute('data-command')) return target;
414
+ target = target.parentNode;
415
+ }
416
+
417
+ return null;
418
+ }
419
+
420
+ /**
421
+ * @description Get the child element of the argument value.
422
+ * A tag that satisfies the query condition is imported.
423
+ * Returns null if not found.
424
+ * @param {Node} node Reference element
425
+ * @param {string|Function|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
426
+ * @param {boolean} last If true returns the last node among the found child nodes. (default: first node)
427
+ * Not use it like jquery.
428
+ * Only one condition can be entered at a time.
429
+ * @returns {Element|null}
430
+ */
431
+ export function getEdgeChild(node, query, last) {
432
+ let check;
433
+
434
+ if (typeof query === 'function') {
435
+ check = query;
436
+ } else if (typeof query === 'object') {
437
+ check = function (current) {
438
+ return current === query;
439
+ };
440
+ } else {
441
+ let attr;
442
+ if (/^\./.test(query)) {
443
+ attr = 'className';
444
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
445
+ } else if (/^#/.test(query)) {
446
+ attr = 'id';
447
+ query = '^' + query.split('#')[1] + '$';
448
+ } else if (/^:/.test(query)) {
449
+ attr = 'name';
450
+ query = '^' + query.split(':')[1] + '$';
451
+ } else {
452
+ attr = 'nodeName';
453
+ query = '^' + (query === 'text' ? '#' + query : query) + '$';
454
+ }
455
+
456
+ const regExp = new RegExp(query, 'i');
457
+ check = function (el) {
458
+ return regExp.test(el[attr]);
459
+ };
460
+ }
461
+
462
+ const childList = getListChildNodes(node, (current) => check(current));
463
+
464
+ return childList[last ? childList.length - 1 : 0];
465
+ }
466
+
467
+ /**
468
+ * @description 1. The first node of all the child nodes of the "first" element is returned.
469
+ * 2. The last node of all the child nodes of the "last" element is returned.
470
+ * 3. When there is no "last" element, the first and last nodes of all the children of the "first" element are returned.
471
+ * { sc: "first", ec: "last" }
472
+ * @param {Node} first First element
473
+ * @param {Node|null} last Last element
474
+ * @returns {{sc: Node, ec: Node}}
475
+ */
476
+ export function getEdgeChildNodes(first, last) {
477
+ if (!first) return;
478
+ if (!last) last = first;
479
+
480
+ while (first && first.nodeType === 1 && first.childNodes.length > 0 && !isBreak(first)) first = first.firstChild;
481
+ while (last && last.nodeType === 1 && last.childNodes.length > 0 && !isBreak(last)) last = last.lastChild;
482
+
483
+ return {
484
+ sc: first,
485
+ ec: last || first
486
+ };
487
+ }
488
+
489
+ /**
490
+ * @description Get the item from the array that matches the condition.
491
+ * @param {Array.<Node>|HTMLCollection|NodeList} array Array to get item
492
+ * @param {Function|null} validation Conditional function
493
+ * @param {boolean} multi If true, returns all items that meet the criteria otherwise, returns an empty array.
494
+ * If false, returns only one item that meet the criteria otherwise return null.
495
+ * @returns {Array.<Node>|null}
496
+ */
497
+ export function getArrayItem(array, validation, multi) {
498
+ if (!array || array.length === 0) return null;
499
+
500
+ validation =
501
+ validation ||
502
+ function () {
503
+ return true;
504
+ };
505
+ const arr = [];
506
+
507
+ for (let i = 0, len = array.length, a; i < len; i++) {
508
+ a = array[i];
509
+ if (validation(a)) {
510
+ if (!multi) return a;
511
+ else arr.push(a);
512
+ }
513
+ }
514
+
515
+ return !multi ? null : arr;
516
+ }
517
+
518
+ /**
519
+ * @description Check if an array contains an element
520
+ * @param {Array.<Node>|HTMLCollection|NodeList} array element array
521
+ * @param {Node} node The node to check for
522
+ * @returns {boolean}
523
+ */
524
+ export function arrayIncludes(array, node) {
525
+ for (let i = 0; i < array.length; i++) {
526
+ if (array[i] === node) {
527
+ return true;
528
+ }
529
+ }
530
+ return false;
531
+ }
532
+
533
+ /**
534
+ * @description Get the index of the argument value in the element array
535
+ * @param {Array|HTMLCollection|NodeList} array element array
536
+ * @param {Node} node The element to find index
537
+ * @returns {number}
538
+ */
539
+ export function getArrayIndex(array, node) {
540
+ let idx = -1;
541
+ for (let i = 0, len = array.length; i < len; i++) {
542
+ if (array[i] === node) {
543
+ idx = i;
544
+ break;
545
+ }
546
+ }
547
+
548
+ return idx;
549
+ }
550
+
551
+ /**
552
+ * @description Get the next index of the argument value in the element array
553
+ * @param {Array.<Node>|HTMLCollection|NodeList} array element array
554
+ * @param {Node} item The element to find index
555
+ * @returns {number}
556
+ */
557
+ export function nextIndex(array, item) {
558
+ const idx = getArrayIndex(array, item);
559
+ if (idx === -1) return -1;
560
+ return idx + 1;
561
+ }
562
+
563
+ /**
564
+ * @description Get the previous index of the argument value in the element array
565
+ * @param {Array.<Node>|HTMLCollection|NodeList} array Element array
566
+ * @param {Node} item The element to find index
567
+ * @returns {number}
568
+ */
569
+ export function prevIndex(array, item) {
570
+ const idx = getArrayIndex(array, item);
571
+ if (idx === -1) return -1;
572
+ return idx - 1;
573
+ }
574
+
575
+ /**
576
+ * @description Add style and className of copyEl to originEl
577
+ * @param {Element} originEl Origin element
578
+ * @param {Element} copyEl Element to copy
579
+ * @param {Array.<string>|null} blacklist Blacklist array(LowerCase)
580
+ */
581
+ export function copyTagAttributes(originEl, copyEl, blacklist) {
582
+ if (copyEl.style.cssText) {
583
+ const copyStyles = copyEl.style;
584
+ for (let i = 0, len = copyStyles.length; i < len; i++) {
585
+ originEl.style[copyStyles[i]] = copyStyles[copyStyles[i]];
586
+ }
587
+ }
588
+
589
+ const attrs = copyEl.attributes;
590
+ for (let i = 0, len = attrs.length, name; i < len; i++) {
591
+ name = attrs[i].name.toLowerCase();
592
+ if (blacklist?.includes(name) || !attrs[i].value) originEl.removeAttribute(name);
593
+ else if (name !== 'style') originEl.setAttribute(attrs[i].name, attrs[i].value);
594
+ }
595
+ }
596
+
597
+ /**
598
+ * @description Copy and apply attributes of format tag that should be maintained. (style, class) Ignore "__se__format__" class
599
+ * @param {Element} originEl Origin element
600
+ * @param {Element} copyEl Element to copy
601
+ */
602
+ export function copyFormatAttributes(originEl, copyEl) {
603
+ copyEl = copyEl.cloneNode(false);
604
+ copyEl.className = copyEl.className.replace(/(\s|^)__se__format__[^\s]+/g, '');
605
+ copyTagAttributes(originEl, copyEl);
606
+ }
607
+
608
+ /**
609
+ * @description Compares the style and class for equal values.
610
+ * Returns true if both are text nodes.
611
+ * @param {Node} a Node to compare
612
+ * @param {Node} b Node to compare
613
+ * @returns {boolean}
614
+ */
615
+ export function isSameAttributes(a, b) {
616
+ if (a.nodeType === 3 && b.nodeType === 3) return true;
617
+ if (a.nodeType === 3 || b.nodeType === 3) return false;
618
+
619
+ const style_a = a.style;
620
+ const style_b = b.style;
621
+ let compStyle = 0;
622
+
623
+ for (let i = 0, len = style_a.length; i < len; i++) {
624
+ if (style_a[style_a[i]] === style_b[style_a[i]]) compStyle++;
625
+ }
626
+
627
+ const class_a = a.classList;
628
+ const class_b = b.classList;
629
+ const wRegExp = RegExp;
630
+ let compClass = 0;
631
+
632
+ for (let i = 0, len = class_a.length; i < len; i++) {
633
+ if (wRegExp('(s|^)' + class_a[i] + '(s|$)').test(class_b.value)) compClass++;
634
+ }
635
+
636
+ return compStyle === style_b.length && compStyle === style_a.length && compClass === class_b.length && compClass === class_a.length;
637
+ }
638
+
639
+ /**
640
+ * @description Delete argumenu value element
641
+ * @param {Node} item Node to be remove
642
+ */
643
+ export function removeItem(item) {
644
+ if (!item) return;
645
+ if (typeof item.remove === 'function') item.remove();
646
+ else if (item.parentNode) item.parentNode.removeChild(item);
647
+ }
648
+
649
+ /**
650
+ * @description Replace element
651
+ * @param {Element} element Target element
652
+ * @param {string|Element} newElement String or element of the new element to apply
653
+ */
654
+ export function changeElement(element, newElement) {
655
+ if (!element) return;
656
+
657
+ if (typeof newElement === 'string') {
658
+ if (element.outerHTML) {
659
+ element.outerHTML = newElement;
660
+ } else {
661
+ const doc = createElement('DIV');
662
+ doc.innerHTML = newElement;
663
+ newElement = doc.firstChild;
664
+ element.parentNode.replaceChild(newElement, element);
665
+ }
666
+ } else if (newElement?.nodeType === 1) {
667
+ element.parentNode.replaceChild(newElement, element);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * @description Set the text content value of the argument value element
673
+ * @param {Node} node Element to replace text content
674
+ * @param {string} txt Text to be applied
675
+ */
676
+ export function changeTxt(node, txt) {
677
+ if (!node || !txt) return;
678
+ node.textContent = txt;
679
+ }
680
+
681
+ /**
682
+ * @description Set style, if all styles are deleted, the style properties are deleted.
683
+ * @param {Element|Element[]} elements Element to set style
684
+ * @param {string} styleName Style attribute name (marginLeft, textAlign...)
685
+ * @param {string|number} value Style value
686
+ */
687
+ export function setStyle(elements, styleName, value) {
688
+ elements = Array.isArray(elements) ? elements : [elements];
689
+
690
+ for (let i = 0, len = elements.length, e; i < len; i++) {
691
+ e = elements[i];
692
+ e.style[styleName] = value;
693
+ if (!value && !e.style.cssText) {
694
+ e.removeAttribute('style');
695
+ }
696
+ }
697
+ }
698
+
699
+ /**
700
+ * @description In the predefined code view mode, the buttons except the executable button are changed to the 'disabled' state.
701
+ * @param {Array.<Element>|HTMLCollection|NodeList} buttonList Button array
702
+ * @param {boolean} disabled Disabled value
703
+ * @param {boolean} important If priveleged mode should be used (Necessary to switch importantDisabled buttons)
704
+ */
705
+ export function setDisabled(buttonList, disabled, important) {
706
+ for (let i = 0, len = buttonList.length; i < len; i++) {
707
+ const button = buttonList[i];
708
+ if (important || !isImportantDisabled(button)) button.disabled = disabled;
709
+ if (important) {
710
+ if (disabled) {
711
+ button.setAttribute('data-important-disabled', '');
712
+ } else {
713
+ button.removeAttribute('data-important-disabled');
714
+ }
715
+ }
716
+ }
717
+ }
718
+
719
+ /**
720
+ * @description Determine whether any of the matched elements are assigned the given class
721
+ * @param {Element} element Elements to search class name
722
+ * @param {string} className Class name to search for
723
+ * @returns {boolean}
724
+ */
725
+ export function hasClass(element, className) {
726
+ if (!element) return;
727
+ const check = new RegExp(`(\\s|^)${className}(\\s|$)`);
728
+ return check.test(element.className);
729
+ }
730
+
731
+ /**
732
+ * @description Append the className value of the argument value element
733
+ * @param {Element|Array.<Element>|NodeList} element Elements to add class name
734
+ * @param {string} className Class name to be add
735
+ */
736
+ export function addClass(element, className) {
737
+ if (!element) return;
738
+
739
+ const elements = element instanceof HTMLCollection || element instanceof NodeList || element instanceof Array ? element : [element];
740
+ const classNames = className.split('|');
741
+
742
+ for (const e of elements) {
743
+ if (!e || e.nodeType !== 1) continue;
744
+ for (const c of classNames) {
745
+ if (c) e.classList.add(c);
746
+ }
747
+ }
748
+ }
749
+
750
+ /**
751
+ * @description Delete the className value of the argument value element
752
+ * @param {Element|Array.<Element>|NodeList} element Elements to remove class name
753
+ * @param {string} className Class name to be remove
754
+ */
755
+ export function removeClass(element, className) {
756
+ if (!element) return;
757
+
758
+ const elements = element instanceof HTMLCollection || element instanceof NodeList || element instanceof Array ? element : [element];
759
+ const classNames = className.split('|');
760
+
761
+ for (const e of elements) {
762
+ if (!e || e.nodeType !== 1) continue;
763
+ for (const c of classNames) {
764
+ if (c) e.classList.remove(c);
765
+ }
766
+ }
767
+ }
768
+
769
+ /**
770
+ * @description Argument value If there is no class name, insert it and delete the class name if it exists
771
+ * @param {Element} element Element to replace class name
772
+ * @param {string} className Class name to be change
773
+ * @returns {boolean|undefined}
774
+ */
775
+ export function toggleClass(element, className) {
776
+ if (!element) return;
777
+ if (element.nodeType !== 1) return;
778
+
779
+ let result = false;
780
+
781
+ const check = new RegExp(`(\\s|^)${className}(\\s|$)`);
782
+ if (check.test(element.className)) {
783
+ element.className = element.className.replace(check, ' ').trim();
784
+ } else {
785
+ element.className += ' ' + className;
786
+ result = true;
787
+ }
788
+
789
+ if (!element.className.trim()) element.removeAttribute('class');
790
+
791
+ return result;
792
+ }
793
+
794
+ /**
795
+ * @description Determine if this offset is the edge offset of container
796
+ * @param {Node} container The node of the selection object. (range.startContainer..)
797
+ * @param {number} offset The offset of the selection object. (core.getRange().startOffset...)
798
+ * @param {string|undefined} dir Select check point - Both edge, Front edge or End edge. ("front": Front edge, "end": End edge, undefined: Both edge)
799
+ * @returns {boolean}
800
+ */
801
+ export function isEdgePoint(container, offset, dir) {
802
+ return (dir !== 'end' && offset === 0) || ((!dir || dir !== 'front') && !container.nodeValue && offset === 1) || ((!dir || dir === 'end') && container.nodeValue && offset === container.nodeValue.length);
803
+ }
804
+
805
+ /**
806
+ * @description It is judged whether it is the edit region top div element or iframe's body tag.
807
+ * @param {Node} element The node to check
808
+ * @returns {boolean}
809
+ */
810
+ export function isWysiwygFrame(element) {
811
+ return element?.nodeType === 1 && (hasClass(element, 'se-wrapper-wysiwyg|sun-editor-carrier-wrapper') || /^BODY$/i.test(element.nodeName));
812
+ }
813
+
814
+ /**
815
+ * @description It is judged whether it is the contenteditable property is false.
816
+ * @param {Node} element The node to check
817
+ * @returns {boolean}
818
+ */
819
+ export function isNonEditable(element) {
820
+ return element?.nodeType === 1 && element.getAttribute('contenteditable') === 'false';
821
+ }
822
+
823
+ /**
824
+ * @description Check the node is a list (ol, ul)
825
+ * @param {Node|string} node The element or element name to check
826
+ * @returns {boolean}
827
+ */
828
+ export function isList(node) {
829
+ return /^(OL|UL)$/i.test(typeof node === 'string' ? node : node?.nodeName);
830
+ }
831
+
832
+ /**
833
+ * @description Check the node is a list cell (li)
834
+ * @param {Node|string} node The element or element name to check
835
+ * @returns {boolean}
836
+ */
837
+ export function isListCell(node) {
838
+ return /^LI$/i.test(typeof node === 'string' ? node : node?.nodeName);
839
+ }
840
+
841
+ /**
842
+ * @description Check the node is a table
843
+ * @param {Node|string} node The element or element name to check
844
+ * @returns {boolean}
845
+ */
846
+ export function isTable(node) {
847
+ return /^TABLE$/i.test(typeof node === 'string' ? node : node?.nodeName);
848
+ }
849
+
850
+ /**
851
+ * @description Check the node is a table elements. (table, thead, tbody, tr, th, td)
852
+ * @param {Node|string} node The element or element name to check
853
+ * @returns {boolean}
854
+ */
855
+ export function isTableElements(node) {
856
+ return /^(TABLE|THEAD|TBODY|TR|TH|TD|COL)$/i.test(typeof node === 'string' ? node : node?.nodeName);
857
+ }
858
+
859
+ /**
860
+ * @description Check the node is a table cell (td, th)
861
+ * @param {Node|string} node The element or element name to check
862
+ * @returns {boolean}
863
+ */
864
+ export function isTableCell(node) {
865
+ return /^(TD|TH)$/i.test(typeof node === 'string' ? node : node?.nodeName);
866
+ }
867
+
868
+ /**
869
+ * @description Check the node is a table row (tr)
870
+ * @param {Node|string} node The element or element name to check
871
+ * @returns {boolean}
872
+ */
873
+ export function isTableRow(node) {
874
+ return /^TR$/i.test(typeof node === 'string' ? node : node?.nodeName);
875
+ }
876
+
877
+ /**
878
+ * @description Check the node is a break node (BR)
879
+ * @param {Node|string} node The element or element name to check
880
+ * @returns {boolean}
881
+ */
882
+ export function isBreak(node) {
883
+ return /^BR$/i.test(typeof node === 'string' ? node : node?.nodeName);
884
+ }
885
+
886
+ /**
887
+ * @description Check the node is a anchor node (A)
888
+ * @param {Node|string} node The element or element name to check
889
+ * @returns {boolean}
890
+ */
891
+ export function isAnchor(node) {
892
+ return /^A$/i.test(typeof node === 'string' ? node : node?.nodeName);
893
+ }
894
+
895
+ /**
896
+ * @description Check the node is a media node (img, iframe, audio, video, canvas)
897
+ * @param {Node|string} node The element or element name to check
898
+ * @returns {boolean}
899
+ */
900
+ export function isMedia(node) {
901
+ return /^(IMG|IFRAME|AUDIO|VIDEO|CANVAS)$/i.test(typeof node === 'string' ? node : node?.nodeName);
902
+ }
903
+
904
+ /**
905
+ * @description Check the node is a figure tag
906
+ * @param {Node|String} node The element or element name to check
907
+ * @returns {Boolean}
908
+ */
909
+ export function isFigure(node) {
910
+ return /^FIGURE$/i.test(typeof node === 'string' ? node : node?.nodeName);
911
+ }
912
+
913
+ /**
914
+ * @description It is judged whether it is the input element (INPUT, TEXTAREA)
915
+ * @param {Node} element The node to check
916
+ * @returns {Boolean}
917
+ */
918
+ export function isInputElement(element) {
919
+ return element?.nodeType === 1 && /^(INPUT|TEXTAREA|SELECT|OPTION)$/i.test(element.nodeName);
920
+ }
921
+
922
+ /**
923
+ * @description Check the line element is empty.
924
+ * @param {Element} element Format element node
925
+ * @returns {boolean}
926
+ */
927
+ export function isEmptyLine(element) {
928
+ return !element?.parentNode || (!element.querySelector('IMG, IFRAME, AUDIO, VIDEO, CANVAS, TABLE') && element.children.length === 0 && isZeroWith(element.textContent));
929
+ }
930
+
931
+ /**
932
+ * @description Check the span's attributes are empty.
933
+ * @param {Element|null} element Element node
934
+ * @returns {Boolean}
935
+ */
936
+ export function isSpanWithoutAttr(element) {
937
+ return element?.nodeType === 1 && /^SPAN$/i.test(element.nodeName) && !element.className && !element.style.cssText;
938
+ }
939
+
940
+ /**
941
+ * @description Checks for "__se__uneditable" in the class list.
942
+ * Components with class "__se__uneditable" cannot be modified.
943
+ * @param {Element} element The element to check
944
+ * @returns {boolean}
945
+ */
946
+ export function isUneditable(element) {
947
+ return hasClass(element, '__se__uneditable');
948
+ }
949
+
950
+ /**
951
+ * @description Checks if element can't be easily enabled
952
+ * @param {Element} element Element to check for
953
+ */
954
+ export function isImportantDisabled(element) {
955
+ return element.hasAttribute('data-important-disabled');
956
+ }
957
+
958
+ /**
959
+ * @description It is judged whether it is the not checking node. (class="katex", "se-exclude-format")
960
+ * @param {Node} element The node to check
961
+ * @returns {boolean}
962
+ */
963
+ export function isExcludeFormat(element) {
964
+ return /(\s|^)(katex|se-exclude-format)(\s|$)/.test(element?.className);
965
+ }
966
+
967
+ /**
968
+ * @description Get nearest scrollable parent
969
+ * @param {Element} element Element
970
+ * @returns {Element|null}
971
+ */
972
+ export function getScrollParent(element) {
973
+ if (!element || /^(body|html)$/i.test(element.nodeName)) {
974
+ return null;
975
+ }
976
+
977
+ if (element.scrollHeight > element.clientHeight) {
978
+ return element;
979
+ } else {
980
+ return getScrollParent(element.parentNode);
981
+ }
982
+ }
983
+
984
+ /**
985
+ * @description Gets the size of the documentElement client size.
986
+ * @param {Document} doc Document object
987
+ * @returns {{w, h}}
988
+ */
989
+ export function getClientSize(doc = _d) {
990
+ return {
991
+ w: doc.documentElement.clientWidth,
992
+ h: doc.documentElement.clientHeight
993
+ };
994
+ }
995
+
996
+ /**
997
+ * @description Gets the size of the window visualViewport size
998
+ * @returns {{top: number, left: number, scale: number}}
999
+ */
1000
+ export function getViewportSize() {
1001
+ if ('visualViewport' in _w) {
1002
+ return {
1003
+ top: _w.visualViewport.pageTop,
1004
+ left: _w.visualViewport.pageLeft,
1005
+ scale: _w.visualViewport.scale
1006
+ };
1007
+ }
1008
+
1009
+ return {
1010
+ top: 0,
1011
+ left: 0,
1012
+ scale: 1
1013
+ };
1014
+ }
1015
+
1016
+ /**
1017
+ * @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child
1018
+ * Returns null if not found.
1019
+ * @param {Node} node Reference element
1020
+ * @param {Node|null} ceiling Highest boundary allowed
1021
+ * @returns {Node|null}
1022
+ */
1023
+ export function getPreviousDeepestNode(node, ceiling) {
1024
+ let previousNode = node.previousSibling;
1025
+ if (!previousNode) {
1026
+ for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
1027
+ if (parentNode === ceiling) return null;
1028
+ if (parentNode.previousSibling) {
1029
+ previousNode = parentNode.previousSibling;
1030
+ break;
1031
+ }
1032
+ }
1033
+ if (!previousNode) return null;
1034
+ }
1035
+
1036
+ if (isNonEditable(previousNode)) return previousNode;
1037
+
1038
+ while (previousNode.lastChild) previousNode = previousNode.lastChild;
1039
+
1040
+ return previousNode;
1041
+ }
1042
+
1043
+ /**
1044
+ * @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child
1045
+ * Returns null if not found.
1046
+ * @param {Node} node Reference element
1047
+ * @param {Node|null} ceiling Highest boundary allowed
1048
+ * @returns {Node|null}
1049
+ */
1050
+ export function getNextDeepestNode(node, ceiling) {
1051
+ let nextNode = node.nextSibling;
1052
+ if (!nextNode) {
1053
+ for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
1054
+ if (parentNode === ceiling) return null;
1055
+ if (parentNode.nextSibling) {
1056
+ nextNode = parentNode.nextSibling;
1057
+ break;
1058
+ }
1059
+ }
1060
+ if (!nextNode) return null;
1061
+ }
1062
+
1063
+ if (isNonEditable(nextNode)) return nextNode;
1064
+
1065
+ while (nextNode.firstChild) nextNode = nextNode.firstChild;
1066
+
1067
+ return nextNode;
1068
+ }
1069
+
1070
+ /**
1071
+ * @description Copies the "wwTarget" element and returns it with inline all styles applied.
1072
+ * @param {Element} wwTarget Target element to copy(.sun-editor.sun-editor-editable)
1073
+ * @param {Boolean} includeWW Include the "wwTarget" element in the copy
1074
+ * @param {string[]} styles Style list - kamel case
1075
+ * @returns
1076
+ */
1077
+ export function applyInlineStylesAll(wwTarget, includeWW, styles) {
1078
+ if (!wwTarget) {
1079
+ console.warn('"parentTarget" is not exist');
1080
+ return null;
1081
+ }
1082
+
1083
+ const tempTarget = _d.createElement('DIV');
1084
+ tempTarget.style.display = 'none';
1085
+
1086
+ if (/body/i.test(wwTarget.nodeName)) {
1087
+ const wwDiv = _d.createElement('DIV');
1088
+ const attrs = wwTarget.attributes;
1089
+ for (let i = 0, len = attrs.length; i < len; i++) {
1090
+ wwDiv.setAttribute(attrs[i].name, attrs[i].value);
1091
+ }
1092
+ wwDiv.innerHTML = wwTarget.innerHTML;
1093
+ wwTarget = wwDiv;
1094
+ } else {
1095
+ wwTarget = wwTarget.cloneNode(true);
1096
+ }
1097
+
1098
+ tempTarget.appendChild(wwTarget);
1099
+ _d.body.appendChild(tempTarget);
1100
+
1101
+ const elements = wwTarget.querySelectorAll('*');
1102
+ for (let i = includeWW ? 0 : 1, el; (el = elements[i]); i++) {
1103
+ const computedStyle = _w.getComputedStyle(el);
1104
+ for (const props of styles || computedStyle) {
1105
+ el.style[props] = computedStyle.getPropertyValue(props) || '';
1106
+ }
1107
+ }
1108
+
1109
+ _d.body.removeChild(tempTarget);
1110
+
1111
+ return wwTarget;
1112
+ }
1113
+
1114
+ const domUtils = {
1115
+ isZeroWith,
1116
+ createElement,
1117
+ createTextNode,
1118
+ getIframeDocument,
1119
+ getAttributesToString,
1120
+ getPositionIndex,
1121
+ getNodePath,
1122
+ getNodeFromPath,
1123
+ getListChildren,
1124
+ getListChildNodes,
1125
+ getNodeDepth,
1126
+ sortNodeByDepth,
1127
+ compareElements,
1128
+ getParentElement,
1129
+ getParentElements,
1130
+ getCommandTarget,
1131
+ getEdgeChild,
1132
+ getEdgeChildNodes,
1133
+ getArrayItem,
1134
+ arrayIncludes,
1135
+ getArrayIndex,
1136
+ nextIndex,
1137
+ prevIndex,
1138
+ copyTagAttributes,
1139
+ copyFormatAttributes,
1140
+ isSameAttributes,
1141
+ removeItem,
1142
+ changeElement,
1143
+ changeTxt,
1144
+ setStyle,
1145
+ setDisabled,
1146
+ hasClass,
1147
+ addClass,
1148
+ removeClass,
1149
+ toggleClass,
1150
+ isEdgePoint,
1151
+ isWysiwygFrame,
1152
+ isNonEditable,
1153
+ isList,
1154
+ isListCell,
1155
+ isTable,
1156
+ isTableElements,
1157
+ isTableCell,
1158
+ isTableRow,
1159
+ isBreak,
1160
+ isAnchor,
1161
+ isMedia,
1162
+ isFigure,
1163
+ isInputElement,
1164
+ isEmptyLine,
1165
+ isSpanWithoutAttr,
1166
+ isUneditable,
1167
+ isImportantDisabled,
1168
+ isExcludeFormat,
1169
+ getScrollParent,
1170
+ getClientSize,
1171
+ getViewportSize,
1172
+ getPreviousDeepestNode,
1173
+ getNextDeepestNode,
1174
+ applyInlineStylesAll
1175
+ };
1176
+
1177
+ export default domUtils;