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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.eslintrc.json +4 -3
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +19 -11
  4. package/README_V3_TEMP.md +705 -0
  5. package/dist/suneditor.min.css +1 -0
  6. package/dist/suneditor.min.js +1 -0
  7. package/example.md +587 -0
  8. package/package.json +15 -9
  9. package/src/assets/icons/_default.js +166 -131
  10. package/src/assets/{suneditor-content.css → suneditor-contents.css} +182 -45
  11. package/src/assets/suneditor.css +1195 -556
  12. package/src/assets/variables.css +138 -0
  13. package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
  14. package/src/core/base/eventHandlers/handler_ww_clipboard.js +29 -4
  15. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +59 -15
  16. package/src/core/base/eventHandlers/handler_ww_key_input.js +426 -212
  17. package/src/core/base/eventHandlers/handler_ww_mouse.js +108 -32
  18. package/src/core/base/eventManager.js +540 -209
  19. package/src/core/base/events.js +616 -320
  20. package/src/core/base/history.js +93 -39
  21. package/src/core/class/char.js +29 -13
  22. package/src/core/class/component.js +332 -145
  23. package/src/core/class/format.js +671 -509
  24. package/src/core/class/html.js +504 -290
  25. package/src/core/class/menu.js +114 -47
  26. package/src/core/class/nodeTransform.js +111 -66
  27. package/src/core/class/offset.js +409 -105
  28. package/src/core/class/selection.js +220 -108
  29. package/src/core/class/shortcuts.js +68 -8
  30. package/src/core/class/toolbar.js +106 -116
  31. package/src/core/class/ui.js +330 -0
  32. package/src/core/class/viewer.js +178 -74
  33. package/src/core/editor.js +489 -384
  34. package/src/core/section/actives.js +118 -22
  35. package/src/core/section/constructor.js +504 -170
  36. package/src/core/section/context.js +28 -23
  37. package/src/core/section/documentType.js +561 -0
  38. package/src/editorInjector/_classes.js +19 -5
  39. package/src/editorInjector/_core.js +71 -7
  40. package/src/editorInjector/index.js +63 -1
  41. package/src/helper/converter.js +137 -19
  42. package/src/helper/dom/domCheck.js +294 -0
  43. package/src/helper/dom/domQuery.js +609 -0
  44. package/src/helper/dom/domUtils.js +533 -0
  45. package/src/helper/dom/index.js +12 -0
  46. package/src/helper/env.js +42 -19
  47. package/src/helper/index.js +7 -4
  48. package/src/helper/keyCodeMap.js +183 -0
  49. package/src/helper/numbers.js +8 -8
  50. package/src/helper/unicode.js +5 -5
  51. package/src/langs/ckb.js +69 -3
  52. package/src/langs/cs.js +67 -1
  53. package/src/langs/da.js +68 -2
  54. package/src/langs/de.js +68 -3
  55. package/src/langs/en.js +29 -1
  56. package/src/langs/es.js +68 -3
  57. package/src/langs/fa.js +70 -2
  58. package/src/langs/fr.js +68 -2
  59. package/src/langs/he.js +68 -3
  60. package/src/langs/hu.js +226 -0
  61. package/src/langs/index.js +3 -2
  62. package/src/langs/it.js +65 -0
  63. package/src/langs/ja.js +68 -3
  64. package/src/langs/ko.js +66 -1
  65. package/src/langs/lv.js +68 -3
  66. package/src/langs/nl.js +68 -3
  67. package/src/langs/pl.js +68 -3
  68. package/src/langs/pt_br.js +65 -0
  69. package/src/langs/ro.js +69 -4
  70. package/src/langs/ru.js +68 -3
  71. package/src/langs/se.js +68 -3
  72. package/src/langs/tr.js +68 -0
  73. package/src/langs/ua.js +68 -3
  74. package/src/langs/ur.js +71 -6
  75. package/src/langs/zh_cn.js +69 -4
  76. package/src/modules/ApiManager.js +77 -54
  77. package/src/modules/Browser.js +667 -0
  78. package/src/modules/ColorPicker.js +162 -102
  79. package/src/modules/Controller.js +233 -136
  80. package/src/modules/Figure.js +913 -489
  81. package/src/modules/FileManager.js +141 -72
  82. package/src/modules/HueSlider.js +113 -61
  83. package/src/modules/Modal.js +292 -113
  84. package/src/modules/ModalAnchorEditor.js +380 -230
  85. package/src/modules/SelectMenu.js +270 -168
  86. package/src/modules/_DragHandle.js +2 -1
  87. package/src/modules/index.js +3 -3
  88. package/src/plugins/browser/audioGallery.js +83 -0
  89. package/src/plugins/browser/fileBrowser.js +103 -0
  90. package/src/plugins/browser/fileGallery.js +83 -0
  91. package/src/plugins/browser/imageGallery.js +81 -0
  92. package/src/plugins/browser/videoGallery.js +103 -0
  93. package/src/plugins/command/blockquote.js +40 -27
  94. package/src/plugins/command/exportPDF.js +134 -0
  95. package/src/plugins/command/fileUpload.js +226 -158
  96. package/src/plugins/command/list_bulleted.js +93 -47
  97. package/src/plugins/command/list_numbered.js +93 -47
  98. package/src/plugins/dropdown/align.js +66 -54
  99. package/src/plugins/dropdown/backgroundColor.js +76 -45
  100. package/src/plugins/dropdown/font.js +71 -47
  101. package/src/plugins/dropdown/fontColor.js +78 -46
  102. package/src/plugins/dropdown/formatBlock.js +74 -33
  103. package/src/plugins/dropdown/hr.js +102 -51
  104. package/src/plugins/dropdown/layout.js +37 -26
  105. package/src/plugins/dropdown/lineHeight.js +54 -38
  106. package/src/plugins/dropdown/list.js +60 -45
  107. package/src/plugins/dropdown/paragraphStyle.js +51 -30
  108. package/src/plugins/dropdown/table.js +1269 -777
  109. package/src/plugins/dropdown/template.js +38 -26
  110. package/src/plugins/dropdown/textStyle.js +43 -31
  111. package/src/plugins/field/mention.js +144 -82
  112. package/src/plugins/index.js +32 -6
  113. package/src/plugins/input/fontSize.js +161 -108
  114. package/src/plugins/input/pageNavigator.js +70 -0
  115. package/src/plugins/modal/audio.js +341 -169
  116. package/src/plugins/modal/drawing.js +530 -0
  117. package/src/plugins/modal/embed.js +886 -0
  118. package/src/plugins/modal/image.js +673 -358
  119. package/src/plugins/modal/link.js +100 -71
  120. package/src/plugins/modal/math.js +384 -168
  121. package/src/plugins/modal/video.js +693 -336
  122. package/src/plugins/popup/anchor.js +222 -0
  123. package/src/suneditor.js +54 -12
  124. package/src/themes/dark.css +85 -0
  125. package/src/typedef.js +86 -0
  126. package/types/assets/icons/_default.d.ts +152 -0
  127. package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
  128. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
  129. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
  130. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
  131. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
  132. package/types/core/base/eventManager.d.ts +377 -0
  133. package/types/core/base/events.d.ts +297 -0
  134. package/types/core/base/history.d.ts +81 -0
  135. package/types/core/class/char.d.ts +60 -0
  136. package/types/core/class/component.d.ts +259 -0
  137. package/types/core/class/format.d.ts +615 -0
  138. package/types/core/class/html.d.ts +377 -0
  139. package/types/core/class/menu.d.ts +118 -0
  140. package/types/core/class/nodeTransform.d.ts +93 -0
  141. package/types/core/class/offset.d.ts +512 -0
  142. package/types/core/class/selection.d.ts +188 -0
  143. package/types/core/class/shortcuts.d.ts +142 -0
  144. package/types/core/class/toolbar.d.ts +189 -0
  145. package/types/core/class/ui.d.ts +144 -0
  146. package/types/core/class/viewer.d.ts +140 -0
  147. package/types/core/editor.d.ts +606 -0
  148. package/types/core/section/actives.d.ts +46 -0
  149. package/types/core/section/constructor.d.ts +748 -0
  150. package/types/core/section/context.d.ts +45 -0
  151. package/types/core/section/documentType.d.ts +178 -0
  152. package/types/editorInjector/_classes.d.ts +41 -0
  153. package/types/editorInjector/_core.d.ts +92 -0
  154. package/types/editorInjector/index.d.ts +71 -0
  155. package/types/helper/converter.d.ts +150 -0
  156. package/types/helper/dom/domCheck.d.ts +182 -0
  157. package/types/helper/dom/domQuery.d.ts +214 -0
  158. package/types/helper/dom/domUtils.d.ts +211 -0
  159. package/types/helper/dom/index.d.ts +9 -0
  160. package/types/helper/env.d.ts +149 -0
  161. package/types/helper/index.d.ts +163 -0
  162. package/types/helper/keyCodeMap.d.ts +110 -0
  163. package/types/helper/numbers.d.ts +43 -0
  164. package/types/helper/unicode.d.ts +28 -0
  165. package/types/index.d.ts +0 -0
  166. package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +170 -103
  167. package/types/langs/ckb.d.ts +384 -0
  168. package/types/langs/cs.d.ts +384 -0
  169. package/types/langs/da.d.ts +384 -0
  170. package/types/langs/de.d.ts +384 -0
  171. package/types/langs/en.d.ts +384 -0
  172. package/types/langs/es.d.ts +384 -0
  173. package/types/langs/fa.d.ts +384 -0
  174. package/types/langs/fr.d.ts +384 -0
  175. package/types/langs/he.d.ts +384 -0
  176. package/types/langs/hu.d.ts +384 -0
  177. package/types/langs/index.d.ts +48 -0
  178. package/types/langs/it.d.ts +384 -0
  179. package/types/langs/ja.d.ts +384 -0
  180. package/types/langs/ko.d.ts +384 -0
  181. package/types/langs/lv.d.ts +384 -0
  182. package/types/langs/nl.d.ts +384 -0
  183. package/types/langs/pl.d.ts +384 -0
  184. package/types/langs/pt_br.d.ts +384 -0
  185. package/types/langs/ro.d.ts +384 -0
  186. package/types/langs/ru.d.ts +384 -0
  187. package/types/langs/se.d.ts +384 -0
  188. package/types/langs/tr.d.ts +384 -0
  189. package/types/langs/ua.d.ts +384 -0
  190. package/types/langs/ur.d.ts +384 -0
  191. package/types/langs/zh_cn.d.ts +384 -0
  192. package/types/modules/ApiManager.d.ts +125 -0
  193. package/types/modules/Browser.d.ts +326 -0
  194. package/types/modules/ColorPicker.d.ts +131 -0
  195. package/types/modules/Controller.d.ts +231 -0
  196. package/types/modules/Figure.d.ts +504 -0
  197. package/types/modules/FileManager.d.ts +202 -0
  198. package/types/modules/HueSlider.d.ts +136 -0
  199. package/types/modules/Modal.d.ts +117 -0
  200. package/types/modules/ModalAnchorEditor.d.ts +236 -0
  201. package/types/modules/SelectMenu.d.ts +194 -0
  202. package/types/modules/_DragHandle.d.ts +7 -0
  203. package/types/modules/index.d.ts +26 -0
  204. package/types/plugins/browser/audioGallery.d.ts +55 -0
  205. package/types/plugins/browser/fileBrowser.d.ts +64 -0
  206. package/types/plugins/browser/fileGallery.d.ts +55 -0
  207. package/types/plugins/browser/imageGallery.d.ts +51 -0
  208. package/types/plugins/browser/videoGallery.d.ts +57 -0
  209. package/types/plugins/command/blockquote.d.ts +28 -0
  210. package/types/plugins/command/exportPDF.d.ts +46 -0
  211. package/types/plugins/command/fileUpload.d.ts +156 -0
  212. package/types/plugins/command/list_bulleted.d.ts +56 -0
  213. package/types/plugins/command/list_numbered.d.ts +56 -0
  214. package/types/plugins/dropdown/align.d.ts +60 -0
  215. package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
  216. package/types/plugins/dropdown/font.d.ts +54 -0
  217. package/types/plugins/dropdown/fontColor.d.ts +63 -0
  218. package/types/plugins/dropdown/formatBlock.d.ts +58 -0
  219. package/types/plugins/dropdown/hr.d.ts +81 -0
  220. package/types/plugins/dropdown/layout.d.ts +40 -0
  221. package/types/plugins/dropdown/lineHeight.d.ts +50 -0
  222. package/types/plugins/dropdown/list.d.ts +39 -0
  223. package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
  224. package/types/plugins/dropdown/table.d.ts +579 -0
  225. package/types/plugins/dropdown/template.d.ts +40 -0
  226. package/types/plugins/dropdown/textStyle.d.ts +41 -0
  227. package/types/plugins/field/mention.d.ts +102 -0
  228. package/types/plugins/index.d.ts +107 -0
  229. package/types/plugins/input/fontSize.d.ts +170 -0
  230. package/types/plugins/input/pageNavigator.d.ts +28 -0
  231. package/types/plugins/modal/audio.d.ts +269 -0
  232. package/types/plugins/modal/drawing.d.ts +246 -0
  233. package/types/plugins/modal/embed.d.ts +387 -0
  234. package/types/plugins/modal/image.d.ts +451 -0
  235. package/types/plugins/modal/link.d.ts +128 -0
  236. package/types/plugins/modal/math.d.ts +193 -0
  237. package/types/plugins/modal/video.d.ts +485 -0
  238. package/types/plugins/popup/anchor.d.ts +56 -0
  239. package/types/suneditor.d.ts +51 -0
  240. package/types/typedef-global.d.ts +144 -0
  241. package/src/core/class/notice.js +0 -42
  242. package/src/helper/domUtils.js +0 -1177
  243. package/src/modules/FileBrowser.js +0 -271
  244. package/src/plugins/command/exportPdf.js +0 -168
  245. package/src/plugins/fileBrowser/imageGallery.js +0 -81
  246. package/src/themes/test.css +0 -61
  247. package/typings/CommandPlugin.d.ts +0 -8
  248. package/typings/DialogPlugin.d.ts +0 -20
  249. package/typings/FileBrowserPlugin.d.ts +0 -30
  250. package/typings/Module.d.ts +0 -15
  251. package/typings/Plugin.d.ts +0 -42
  252. package/typings/SubmenuPlugin.d.ts +0 -8
  253. package/typings/_classes.d.ts +0 -17
  254. package/typings/_colorPicker.d.ts +0 -60
  255. package/typings/_core.d.ts +0 -55
  256. package/typings/align.d.ts +0 -5
  257. package/typings/audio.d.ts +0 -5
  258. package/typings/backgroundColor.d.ts +0 -5
  259. package/typings/blockquote.d.ts +0 -5
  260. package/typings/char.d.ts +0 -39
  261. package/typings/component.d.ts +0 -38
  262. package/typings/context.d.ts +0 -39
  263. package/typings/converter.d.ts +0 -33
  264. package/typings/dialog.d.ts +0 -28
  265. package/typings/domUtils.d.ts +0 -361
  266. package/typings/editor.d.ts +0 -7
  267. package/typings/editor.ts +0 -542
  268. package/typings/env.d.ts +0 -70
  269. package/typings/eventManager.d.ts +0 -37
  270. package/typings/events.d.ts +0 -262
  271. package/typings/fileBrowser.d.ts +0 -42
  272. package/typings/fileManager.d.ts +0 -67
  273. package/typings/font.d.ts +0 -5
  274. package/typings/fontColor.d.ts +0 -5
  275. package/typings/fontSize.d.ts +0 -5
  276. package/typings/format.d.ts +0 -191
  277. package/typings/formatBlock.d.ts +0 -5
  278. package/typings/history.d.ts +0 -48
  279. package/typings/horizontalRule.d.ts +0 -5
  280. package/typings/image.d.ts +0 -5
  281. package/typings/imageGallery.d.ts +0 -5
  282. package/typings/index.d.ts +0 -21
  283. package/typings/index.modules.d.ts +0 -11
  284. package/typings/index.plugins.d.ts +0 -58
  285. package/typings/lineHeight.d.ts +0 -5
  286. package/typings/link.d.ts +0 -5
  287. package/typings/list.d.ts +0 -5
  288. package/typings/math.d.ts +0 -5
  289. package/typings/mediaContainer.d.ts +0 -25
  290. package/typings/mention.d.ts +0 -5
  291. package/typings/node.d.ts +0 -57
  292. package/typings/notice.d.ts +0 -16
  293. package/typings/numbers.d.ts +0 -29
  294. package/typings/offset.d.ts +0 -24
  295. package/typings/options.d.ts +0 -589
  296. package/typings/paragraphStyle.d.ts +0 -5
  297. package/typings/resizing.d.ts +0 -141
  298. package/typings/selection.d.ts +0 -94
  299. package/typings/shortcuts.d.ts +0 -13
  300. package/typings/suneditor.d.ts +0 -9
  301. package/typings/table.d.ts +0 -5
  302. package/typings/template.d.ts +0 -5
  303. package/typings/textStyle.d.ts +0 -5
  304. package/typings/toolbar.d.ts +0 -32
  305. package/typings/unicode.d.ts +0 -25
  306. package/typings/video.d.ts +0 -5
@@ -0,0 +1,609 @@
1
+ /**
2
+ * @fileoverview Implements Helper for querying the DOM.
3
+ */
4
+
5
+ import { zeroWidthRegExp } from '../unicode';
6
+ import domUtils from './domUtils';
7
+ import domCheck from './domCheck';
8
+
9
+ /**
10
+ * @description Returns the index compared to other sibling nodes.
11
+ * @param {Node} node The Node to find index
12
+ * @returns {number}
13
+ */
14
+ export function getPositionIndex(node) {
15
+ let idx = 0;
16
+ while ((node = node.previousSibling)) {
17
+ idx += 1;
18
+ }
19
+ return idx;
20
+ }
21
+
22
+ /**
23
+ * @description Returns the position of the "node" in the "parentNode" in a numerical array.
24
+ * - e.g.) <p><span>aa</span><span>bb</span></p> : getNodePath(node: "bb", parentNode: "<P>") -> [1, 0]
25
+ * @param {Node} node The Node to find position path
26
+ * @param {?Node} parentNode Parent node. If null, wysiwyg div area
27
+ * @param {?{s: number, e: number}=} _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.
28
+ * "_newOffsets.s" stores the length of the combined characters after "node" and "_newOffsets.e" stores the length of the combined characters before "node".
29
+ * Do not use unless absolutely necessary.
30
+ * @returns {Array<number>}
31
+ */
32
+ export function getNodePath(node, parentNode, _newOffsets) {
33
+ const path = [];
34
+ let finds = true;
35
+
36
+ getParentElement(node, (el) => {
37
+ if (el === parentNode) finds = false;
38
+ if (finds && !domCheck.isWysiwygFrame(el)) {
39
+ // merge text nodes
40
+ if (_newOffsets && el.nodeType === 3) {
41
+ let temp = null,
42
+ tempText = null;
43
+ _newOffsets.s = _newOffsets.e = 0;
44
+
45
+ let previous = el.previousSibling;
46
+ while (previous?.nodeType === 3) {
47
+ tempText = previous.textContent.replace(zeroWidthRegExp, '');
48
+ _newOffsets.s += tempText.length;
49
+ el.textContent = tempText + el.textContent;
50
+ temp = previous;
51
+ previous = previous.previousSibling;
52
+ domUtils.removeItem(temp);
53
+ }
54
+
55
+ let next = el.nextSibling;
56
+ while (next?.nodeType === 3) {
57
+ tempText = next.textContent.replace(zeroWidthRegExp, '');
58
+ _newOffsets.e += tempText.length;
59
+ el.textContent += tempText;
60
+ temp = next;
61
+ next = next.nextSibling;
62
+ domUtils.removeItem(temp);
63
+ }
64
+ }
65
+
66
+ // index push
67
+ path.push(el);
68
+ }
69
+ return false;
70
+ });
71
+
72
+ return path.map(getPositionIndex).reverse();
73
+ }
74
+
75
+ /**
76
+ * @template {Node} T
77
+ * @description Returns the node in the location of the path array obtained from "helper.dom.getNodePath".
78
+ * @param {Array<number>} offsets Position array, array obtained from "helper.dom.getNodePath"
79
+ * @param {Node} parentNode Base parent element
80
+ * @returns {T}
81
+ */
82
+ export function getNodeFromPath(offsets, parentNode) {
83
+ let current = parentNode;
84
+ let nodes;
85
+
86
+ for (let i = 0, len = offsets.length; i < len; i++) {
87
+ nodes = current.childNodes;
88
+ if (nodes.length === 0) break;
89
+ if (nodes.length <= offsets[i]) {
90
+ current = nodes[nodes.length - 1];
91
+ } else {
92
+ current = nodes[offsets[i]];
93
+ }
94
+ }
95
+
96
+ return /** @type {T} */ (current);
97
+ }
98
+
99
+ /**
100
+ * @template {HTMLElement} T
101
+ * @description Get all "children" of the argument value element (Without text nodes)
102
+ * @param {Node} element element to get child node
103
+ * @param {?(current: *) => boolean} validation Conditional function
104
+ * @returns {Array<T>}
105
+ */
106
+ export function getListChildren(element, validation) {
107
+ /** @type {Array<T>} */
108
+ const children = [];
109
+ if (!element) return children;
110
+
111
+ const el = /** @type {Element} */ (element);
112
+ if (!el.children || el.children.length === 0) return children;
113
+
114
+ validation =
115
+ validation ||
116
+ function () {
117
+ return true;
118
+ };
119
+
120
+ (function recursionFunc(current) {
121
+ if (el !== current && validation(current)) {
122
+ children.push(/** @type {T} */ (current));
123
+ }
124
+
125
+ if (current.children) {
126
+ for (let i = 0, len = current.children.length; i < len; i++) {
127
+ recursionFunc(current.children[i]);
128
+ }
129
+ }
130
+ })(el);
131
+
132
+ return /** @type {Array<T>} */ (children);
133
+ }
134
+
135
+ /**
136
+ * @template {Node} T
137
+ * @description Get all "childNodes" of the argument value element (Include text nodes)
138
+ * @param {Node} element element to get child node
139
+ * @param {?(current: *) => boolean} validation Conditional function
140
+ * @returns {Array<T>}
141
+ */
142
+ export function getListChildNodes(element, validation) {
143
+ const children = [];
144
+ if (!element || element.childNodes.length === 0) return children;
145
+
146
+ validation =
147
+ validation ||
148
+ function () {
149
+ return true;
150
+ };
151
+
152
+ (function recursionFunc(current) {
153
+ if (element !== current && validation(current)) {
154
+ children.push(current);
155
+ }
156
+
157
+ for (let i = 0, len = current.childNodes.length; i < len; i++) {
158
+ recursionFunc(current.childNodes[i]);
159
+ }
160
+ })(element);
161
+
162
+ return /** @type {Array<T>} */ (children);
163
+ }
164
+
165
+ /**
166
+ * @description Returns the number of parents nodes.
167
+ * - "0" when the parent node is the WYSIWYG area.
168
+ * - '-1' when the element argument is the WYSIWYG area.
169
+ * @param {Node} node The element to check
170
+ * @returns {number}
171
+ */
172
+ export function getNodeDepth(node) {
173
+ if (!node || domCheck.isWysiwygFrame(node)) return -1;
174
+
175
+ let depth = 0;
176
+ node = node.parentNode;
177
+
178
+ while (node && !domCheck.isWysiwygFrame(node)) {
179
+ depth += 1;
180
+ node = node.parentNode;
181
+ }
182
+
183
+ return depth;
184
+ }
185
+
186
+ /**
187
+ * @description Sort a node array by depth of element.
188
+ * @param {Array<Node>} array Node array
189
+ * @param {boolean} des true: descending order / false: ascending order
190
+ */
191
+ export function sortNodeByDepth(array, des) {
192
+ const t = !des ? -1 : 1;
193
+ const f = t * -1;
194
+
195
+ array.sort(function (a, b) {
196
+ if (!domCheck.isListCell(a) || !domCheck.isListCell(b)) return 0;
197
+ const a_i = getNodeDepth(a);
198
+ const b_i = getNodeDepth(b);
199
+ return a_i > b_i ? t : a_i < b_i ? f : 0;
200
+ });
201
+ }
202
+
203
+ /**
204
+ * @description Compares two elements to find a common ancestor, and returns the order of the two elements.
205
+ * @param {Node} a Node to compare.
206
+ * @param {Node} b Node to compare.
207
+ * @returns {{ancestor: HTMLElement|null, a: Node, b: Node, result: number}} { ancesstor, a, b, result: (a > b ? 1 : a < b ? -1 : 0) };
208
+ */
209
+ export function compareElements(a, b) {
210
+ let aNode = a,
211
+ bNode = b;
212
+ while (aNode && bNode && aNode.parentElement !== bNode.parentElement) {
213
+ aNode = aNode.parentElement;
214
+ bNode = bNode.parentElement;
215
+ }
216
+
217
+ if (!aNode || !bNode)
218
+ return {
219
+ ancestor: null,
220
+ a: a,
221
+ b: b,
222
+ result: 0
223
+ };
224
+
225
+ const children = aNode.parentNode.childNodes;
226
+ const aIndex = domUtils.getArrayIndex(children, aNode);
227
+ const bIndex = domUtils.getArrayIndex(children, bNode);
228
+
229
+ return {
230
+ ancestor: aNode.parentElement,
231
+ a: aNode,
232
+ b: bNode,
233
+ result: aIndex > bIndex ? 1 : aIndex < bIndex ? -1 : 0
234
+ };
235
+ }
236
+
237
+ /**
238
+ * @template {HTMLElement} T
239
+ * @description Get the parent element of the argument value.
240
+ * - A tag that satisfies the query condition is imported.
241
+ * @param {Node} element Reference element
242
+ * @param {string|((current: *) => boolean)|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
243
+ * - Not use it like jquery.
244
+ * - Only one condition can be entered at a time.
245
+ * @param {?number=} depth Number of parent levels to depth.
246
+ * @returns {T|null} Not found: null
247
+ */
248
+ export function getParentElement(element, query, depth) {
249
+ let valid;
250
+
251
+ if (typeof query === 'function') {
252
+ valid = query;
253
+ } else if (typeof query === 'object') {
254
+ /** @param {Node} current */
255
+ valid = (current) => current === query;
256
+ } else {
257
+ let attr;
258
+ if (/^\./.test(query)) {
259
+ attr = 'className';
260
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
261
+ } else if (/^#/.test(query)) {
262
+ attr = 'id';
263
+ query = '^' + query.split('#')[1] + '$';
264
+ } else if (/^:/.test(query)) {
265
+ attr = 'name';
266
+ query = '^' + query.split(':')[1] + '$';
267
+ } else {
268
+ attr = 'nodeName';
269
+ query = '^' + query + '$';
270
+ }
271
+
272
+ const regExp = new RegExp(query, 'i');
273
+ /** @param {Node} el */
274
+ valid = (el) => regExp.test(el[attr]);
275
+ }
276
+
277
+ if (!depth) depth = Infinity;
278
+ let index = 0;
279
+ while (element && !valid(element)) {
280
+ if (index >= depth || domCheck.isWysiwygFrame(element)) {
281
+ return null;
282
+ }
283
+ element = element.parentElement;
284
+ index++;
285
+ }
286
+
287
+ return /** @type {T} */ (element);
288
+ }
289
+
290
+ /**
291
+ * @template {HTMLElement} T
292
+ * @description Gets all ancestors of the argument value.
293
+ * - Get all tags that satisfy the query condition.
294
+ * @param {Node} element Reference element
295
+ * @param {string|((current: *) => boolean)|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
296
+ * Not use it like jquery.
297
+ * Only one condition can be entered at a time.
298
+ * @param {?number=} depth Number of parent levels to depth.
299
+ * @returns {Array<T>} Returned in an array in order.
300
+ */
301
+ export function getParentElements(element, query, depth) {
302
+ let valid;
303
+
304
+ if (typeof query === 'function') {
305
+ valid = query;
306
+ } else if (typeof query === 'object') {
307
+ /** @param {Node} current */
308
+ valid = (current) => current === query;
309
+ } else {
310
+ let attr;
311
+ if (/^\./.test(query)) {
312
+ attr = 'className';
313
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
314
+ } else if (/^#/.test(query)) {
315
+ attr = 'id';
316
+ query = '^' + query.split('#')[1] + '$';
317
+ } else if (/^:/.test(query)) {
318
+ attr = 'name';
319
+ query = '^' + query.split(':')[1] + '$';
320
+ } else {
321
+ attr = 'nodeName';
322
+ query = '^' + query + '$';
323
+ }
324
+
325
+ const regExp = new RegExp(query, 'i');
326
+ /** @param {Node} el */
327
+ valid = (el) => regExp.test(el[attr]);
328
+ }
329
+
330
+ const elementList = [];
331
+ if (!depth) depth = Infinity;
332
+ let index = 0;
333
+ while (index <= depth && element && !domCheck.isWysiwygFrame(element)) {
334
+ if (valid(element)) {
335
+ elementList.push(element);
336
+ }
337
+ element = element.parentElement;
338
+ index++;
339
+ }
340
+
341
+ return /** @type {Array<T>} */ (elementList);
342
+ }
343
+
344
+ /**
345
+ * @template {HTMLElement} T
346
+ * @description Gets the element with "data-command" attribute among the parent elements.
347
+ * @param {Node} target Target element
348
+ * @returns {T|null}
349
+ */
350
+ export function getCommandTarget(target) {
351
+ let n = /** @type {HTMLElement} */ (target);
352
+ while (n && !/^(UL)$/i.test(n.nodeName) && !domUtils.hasClass(n, 'sun-editor')) {
353
+ if (n.hasAttribute('data-command')) return /** @type {T} */ (n);
354
+ n = n.parentElement;
355
+ }
356
+
357
+ return null;
358
+ }
359
+
360
+ /**
361
+ * @template {HTMLElement} T
362
+ * @description Get the event.target element.
363
+ * @param {Event} event Event object
364
+ * @returns {T|null}
365
+ */
366
+ export function getEventTarget(event) {
367
+ return /** @type {T} */ (event.target);
368
+ }
369
+
370
+ /**
371
+ * @template {Node} T
372
+ * @description Get the child element of the argument value.
373
+ * - A tag that satisfies the query condition is imported.
374
+ * @param {Node} node Reference element
375
+ * @param {string|((current: *) => boolean)|Node} query Query String (nodeName, .className, #ID, :name) or validation function.
376
+ * @param {boolean} last If true returns the last node among the found child nodes. (default: first node)
377
+ * Not use it like jquery.
378
+ * Only one condition can be entered at a time.
379
+ * @returns {T|null} Not found: null
380
+ */
381
+ export function getEdgeChild(node, query, last) {
382
+ let valid;
383
+
384
+ if (typeof query === 'function') {
385
+ valid = query;
386
+ } else if (typeof query === 'object') {
387
+ valid = function (current) {
388
+ return current === query;
389
+ };
390
+ } else {
391
+ let attr;
392
+ if (/^\./.test(query)) {
393
+ attr = 'className';
394
+ query = '(\\s|^)' + query.split('.')[1] + '(\\s|$)';
395
+ } else if (/^#/.test(query)) {
396
+ attr = 'id';
397
+ query = '^' + query.split('#')[1] + '$';
398
+ } else if (/^:/.test(query)) {
399
+ attr = 'name';
400
+ query = '^' + query.split(':')[1] + '$';
401
+ } else {
402
+ attr = 'nodeName';
403
+ query = '^' + (query === 'text' ? '#' + query : query) + '$';
404
+ }
405
+
406
+ const regExp = new RegExp(query, 'i');
407
+ valid = function (el) {
408
+ return regExp.test(el[attr]);
409
+ };
410
+ }
411
+
412
+ const childList = getListChildNodes(node, (current) => valid(current));
413
+
414
+ return /** @type {T} */ (childList[last ? childList.length - 1 : 0]);
415
+ }
416
+
417
+ /**
418
+ * @description Get edge child nodes of the argument value.
419
+ * - 1. The first node of all the child nodes of the "first" element is returned.
420
+ * - 2. The last node of all the child nodes of the "last" element is returned.
421
+ * - 3. When there is no "last" element, the first and last nodes of all the children of the "first" element are returned.
422
+ * @param {Node} first First element
423
+ * @param {Node|null} last Last element
424
+ * @returns {{sc: Node, ec: Node}} { sc: "first", ec: "last" }
425
+ */
426
+ export function getEdgeChildNodes(first, last) {
427
+ if (!first) return;
428
+ if (!last) last = first;
429
+
430
+ while (first && first.nodeType === 1 && first.childNodes.length > 0 && !domCheck.isBreak(first)) first = first.firstChild;
431
+ while (last && last.nodeType === 1 && last.childNodes.length > 0 && !domCheck.isBreak(last)) last = last.lastChild;
432
+
433
+ return {
434
+ sc: first,
435
+ ec: last || first
436
+ };
437
+ }
438
+
439
+ /**
440
+ * @template {Node} T
441
+ * @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child
442
+ * @param {Node} node Reference element
443
+ * @param {?Node=} ceiling Highest boundary allowed
444
+ * @returns {T|null} Not found: null
445
+ */
446
+ export function getPreviousDeepestNode(node, ceiling) {
447
+ let previousNode = node.previousSibling;
448
+ if (!previousNode) {
449
+ for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
450
+ if (parentNode === ceiling) return null;
451
+ if (parentNode.previousSibling) {
452
+ previousNode = parentNode.previousSibling;
453
+ break;
454
+ }
455
+ }
456
+ if (!previousNode) return null;
457
+ }
458
+
459
+ if (domCheck.isNonEditable(previousNode)) return /** @type {T} */ (/** @type {unknown} */ (previousNode));
460
+
461
+ while (previousNode.lastChild) previousNode = previousNode.lastChild;
462
+
463
+ return /** @type {T} */ (/** @type {unknown} */ (previousNode));
464
+ }
465
+
466
+ /**
467
+ * @template {Node} T
468
+ * @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child
469
+ * @param {Node} node Reference element
470
+ * @param {?Node=} ceiling Highest boundary allowed
471
+ * @returns {T|null} Not found: null
472
+ */
473
+ export function getNextDeepestNode(node, ceiling) {
474
+ let nextNode = node.nextSibling;
475
+ if (!nextNode) {
476
+ for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
477
+ if (parentNode === ceiling) return null;
478
+ if (parentNode.nextSibling) {
479
+ nextNode = parentNode.nextSibling;
480
+ break;
481
+ }
482
+ }
483
+ if (!nextNode) return null;
484
+ }
485
+
486
+ if (domCheck.isNonEditable(nextNode)) return /** @type {T} */ (/** @type {unknown} */ (nextNode));
487
+
488
+ while (nextNode.firstChild) nextNode = nextNode.firstChild;
489
+
490
+ return /** @type {T} */ (/** @type {unknown} */ (nextNode));
491
+ }
492
+
493
+ /**
494
+ * @description Find the index of the text node in the line element.
495
+ * @param {Node} line Line element (p, div, etc.)
496
+ * @param {Node} offsetContainer Base node to start searching
497
+ * @param {number} offset Base offset to start searching
498
+ * @param {?(current: *) => boolean=} validate Validation function
499
+ * @returns {number}
500
+ */
501
+ export function findTextIndexOnLine(line, offsetContainer, offset, validate) {
502
+ if (!line) return 0;
503
+ if (!validate) validate = () => true;
504
+
505
+ let index = 0;
506
+ let found = false;
507
+
508
+ (function recursionFunc(node) {
509
+ if (found || node.nodeType === 8) return;
510
+ if (validate(node)) return; // component.is
511
+
512
+ if (node.nodeType === 3) {
513
+ if (node === offsetContainer) {
514
+ index += offset;
515
+ found = true;
516
+ return;
517
+ }
518
+ index += node.textContent.length;
519
+ } else if (node.nodeType === 1) {
520
+ const childNodes = node.childNodes;
521
+ for (let i = 0, len = childNodes.length; i < len; i++) {
522
+ recursionFunc(childNodes[i]);
523
+ if (found) return;
524
+ }
525
+ }
526
+ })(line);
527
+
528
+ return index;
529
+ }
530
+
531
+ /**
532
+ * @description Find the end index of a sequence of at least minTabSize consecutive non-breaking spaces or spaces
533
+ * - which are interpreted as a tab key, occurring after a given base index in a text string.
534
+ * @param {Node} line Line element (p, div, etc.)
535
+ * @param {number} baseIndex Base index to start searching
536
+ * @param {number} minTabSize Minimum number of consecutive spaces to consider as a tab
537
+ * @returns {number} The adjusted index within the line element accounting for non-space characters
538
+ */
539
+ export function findTabEndIndex(line, baseIndex, minTabSize) {
540
+ if (!line) return 0;
541
+ const innerText = line.textContent;
542
+ const regex = new RegExp(`((\\u00A0|\\s){${minTabSize},})`, 'g');
543
+ let match;
544
+
545
+ regex.lastIndex = baseIndex;
546
+
547
+ while ((match = regex.exec(innerText)) !== null) {
548
+ if (match.index >= baseIndex) {
549
+ const spaceEndIndex = match.index + match[0].length - 1;
550
+ const precedingText = innerText.slice(0, spaceEndIndex + 1);
551
+ const nonSpaceCharCount = (precedingText.match(/[^\u00A0\s]/g) || []).length;
552
+ return spaceEndIndex + nonSpaceCharCount + minTabSize;
553
+ }
554
+ }
555
+
556
+ return 0;
557
+ }
558
+
559
+ /**
560
+ * @description Get nearest scrollable parent
561
+ * @param {Node} element Element
562
+ * @returns {HTMLElement|null}
563
+ */
564
+ export function getScrollParent(element) {
565
+ if (!element || /^(body|html)$/i.test(element.nodeName)) {
566
+ return null;
567
+ }
568
+
569
+ const el = /** @type {HTMLElement} */ (element);
570
+ if (el.scrollHeight > el.clientHeight) {
571
+ return el;
572
+ } else {
573
+ return getScrollParent(el.parentNode);
574
+ }
575
+ }
576
+
577
+ /**
578
+ * @description Get the argument iframe's document object if use the "iframe" or "fullPage" options
579
+ * @param {HTMLIFrameElement} iframe Iframe element (this.editor.frameContext.get('wysiwygFrame'))
580
+ * @returns {Document}
581
+ */
582
+ export function getIframeDocument(iframe) {
583
+ return iframe.contentWindow?.document || iframe.contentDocument;
584
+ }
585
+
586
+ const query = {
587
+ getPositionIndex,
588
+ getNodePath,
589
+ getNodeFromPath,
590
+ getListChildren,
591
+ getListChildNodes,
592
+ getNodeDepth,
593
+ sortNodeByDepth,
594
+ compareElements,
595
+ getParentElement,
596
+ getParentElements,
597
+ getCommandTarget,
598
+ getEventTarget,
599
+ getEdgeChild,
600
+ getEdgeChildNodes,
601
+ getPreviousDeepestNode,
602
+ getNextDeepestNode,
603
+ findTextIndexOnLine,
604
+ findTabEndIndex,
605
+ getScrollParent,
606
+ getIframeDocument
607
+ };
608
+
609
+ export default query;