suneditor 3.0.0-beta.9 → 3.0.0-rc.2

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 (380) hide show
  1. package/README.md +65 -57
  2. package/dist/suneditor-contents.min.css +1 -0
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +110 -61
  6. package/src/assets/design/color.css +36 -17
  7. package/src/assets/design/size.css +2 -0
  8. package/src/assets/icons/defaultIcons.js +17 -2
  9. package/src/assets/suneditor-contents.css +51 -16
  10. package/src/assets/suneditor.css +116 -43
  11. package/src/core/config/contextProvider.js +288 -0
  12. package/src/core/config/eventManager.js +188 -0
  13. package/src/core/config/instanceCheck.js +59 -0
  14. package/src/core/config/optionProvider.js +452 -0
  15. package/src/core/editor.js +166 -1637
  16. package/src/core/event/actions/index.js +229 -0
  17. package/src/core/event/effects/common.registry.js +74 -0
  18. package/src/core/event/effects/keydown.registry.js +573 -0
  19. package/src/core/event/effects/ruleHelpers.js +148 -0
  20. package/src/core/event/eventOrchestrator.js +944 -0
  21. package/src/core/event/executor.js +27 -0
  22. package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +27 -28
  23. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +10 -8
  24. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +22 -23
  25. package/src/core/event/handlers/handler_ww_input.js +75 -0
  26. package/src/core/event/handlers/handler_ww_key.js +228 -0
  27. package/src/core/event/handlers/handler_ww_mouse.js +166 -0
  28. package/src/core/event/ports.js +211 -0
  29. package/src/core/event/reducers/keydown.reducer.js +97 -0
  30. package/src/core/event/rules/keydown.rule.arrow.js +63 -0
  31. package/src/core/event/rules/keydown.rule.backspace.js +208 -0
  32. package/src/core/event/rules/keydown.rule.delete.js +132 -0
  33. package/src/core/event/rules/keydown.rule.enter.js +150 -0
  34. package/src/core/event/rules/keydown.rule.tab.js +35 -0
  35. package/src/core/event/support/defaultLineManager.js +136 -0
  36. package/src/core/event/support/selectionState.js +204 -0
  37. package/src/core/kernel/coreKernel.js +320 -0
  38. package/src/core/kernel/kernelInjector.js +19 -0
  39. package/src/core/kernel/store.js +173 -0
  40. package/src/core/{class → logic/dom}/char.js +42 -45
  41. package/src/core/logic/dom/format.js +1075 -0
  42. package/src/core/{class → logic/dom}/html.js +743 -624
  43. package/src/core/logic/dom/inline.js +1847 -0
  44. package/src/core/logic/dom/listFormat.js +601 -0
  45. package/src/core/{class → logic/dom}/nodeTransform.js +92 -72
  46. package/src/core/{class → logic/dom}/offset.js +254 -317
  47. package/src/core/logic/dom/selection.js +754 -0
  48. package/src/core/logic/panel/menu.js +389 -0
  49. package/src/core/logic/panel/toolbar.js +449 -0
  50. package/src/core/logic/panel/viewer.js +761 -0
  51. package/src/core/logic/shell/_commandExecutor.js +380 -0
  52. package/src/core/logic/shell/commandDispatcher.js +241 -0
  53. package/src/core/logic/shell/component.js +970 -0
  54. package/src/core/logic/shell/focusManager.js +110 -0
  55. package/src/core/{base → logic/shell}/history.js +110 -60
  56. package/src/core/logic/shell/pluginManager.js +363 -0
  57. package/src/core/logic/shell/shortcuts.js +130 -0
  58. package/src/core/logic/shell/ui.js +904 -0
  59. package/src/core/schema/context.js +66 -0
  60. package/src/core/schema/frameContext.js +160 -0
  61. package/src/core/schema/options.js +628 -0
  62. package/src/core/section/constructor.js +194 -500
  63. package/src/core/section/documentType.js +297 -222
  64. package/src/events.js +808 -543
  65. package/src/helper/clipboard.js +27 -16
  66. package/src/helper/converter.js +100 -78
  67. package/src/helper/dom/domCheck.js +56 -30
  68. package/src/helper/dom/domQuery.js +159 -89
  69. package/src/helper/dom/domUtils.js +114 -49
  70. package/src/helper/dom/index.js +5 -1
  71. package/src/helper/env.js +26 -26
  72. package/src/helper/index.js +1 -1
  73. package/src/helper/keyCodeMap.js +25 -28
  74. package/src/helper/numbers.js +4 -8
  75. package/src/helper/unicode.js +4 -8
  76. package/src/hooks/base.js +307 -0
  77. package/src/hooks/params.js +130 -0
  78. package/src/interfaces/contracts.js +227 -0
  79. package/src/interfaces/index.js +7 -0
  80. package/src/interfaces/plugins.js +239 -0
  81. package/src/langs/ckb.js +4 -4
  82. package/src/langs/cs.js +4 -4
  83. package/src/langs/da.js +4 -4
  84. package/src/langs/de.js +4 -4
  85. package/src/langs/en.js +4 -4
  86. package/src/langs/es.js +4 -4
  87. package/src/langs/fa.js +4 -4
  88. package/src/langs/fr.js +4 -4
  89. package/src/langs/he.js +4 -4
  90. package/src/langs/hu.js +4 -4
  91. package/src/langs/it.js +4 -4
  92. package/src/langs/ja.js +4 -4
  93. package/src/langs/km.js +4 -4
  94. package/src/langs/ko.js +4 -4
  95. package/src/langs/lv.js +4 -4
  96. package/src/langs/nl.js +4 -4
  97. package/src/langs/pl.js +4 -4
  98. package/src/langs/pt_br.js +13 -13
  99. package/src/langs/ro.js +4 -4
  100. package/src/langs/ru.js +4 -4
  101. package/src/langs/se.js +4 -4
  102. package/src/langs/tr.js +4 -4
  103. package/src/langs/uk.js +4 -4
  104. package/src/langs/ur.js +4 -4
  105. package/src/langs/zh_cn.js +4 -4
  106. package/src/modules/{Browser.js → contract/Browser.js} +119 -128
  107. package/src/modules/{ColorPicker.js → contract/ColorPicker.js} +132 -142
  108. package/src/modules/contract/Controller.js +589 -0
  109. package/src/modules/{Figure.js → contract/Figure.js} +591 -411
  110. package/src/modules/{HueSlider.js → contract/HueSlider.js} +125 -86
  111. package/src/modules/contract/Modal.js +357 -0
  112. package/src/modules/contract/index.js +9 -0
  113. package/src/modules/manager/ApiManager.js +197 -0
  114. package/src/modules/{FileManager.js → manager/FileManager.js} +128 -160
  115. package/src/modules/manager/index.js +5 -0
  116. package/src/modules/{ModalAnchorEditor.js → ui/ModalAnchorEditor.js} +108 -138
  117. package/src/modules/{SelectMenu.js → ui/SelectMenu.js} +119 -120
  118. package/src/modules/{_DragHandle.js → ui/_DragHandle.js} +1 -1
  119. package/src/modules/ui/index.js +6 -0
  120. package/src/plugins/browser/audioGallery.js +23 -26
  121. package/src/plugins/browser/fileBrowser.js +25 -28
  122. package/src/plugins/browser/fileGallery.js +20 -23
  123. package/src/plugins/browser/imageGallery.js +24 -23
  124. package/src/plugins/browser/videoGallery.js +27 -29
  125. package/src/plugins/command/blockquote.js +11 -17
  126. package/src/plugins/command/exportPDF.js +26 -26
  127. package/src/plugins/command/fileUpload.js +138 -133
  128. package/src/plugins/command/list_bulleted.js +48 -44
  129. package/src/plugins/command/list_numbered.js +48 -44
  130. package/src/plugins/dropdown/align.js +64 -50
  131. package/src/plugins/dropdown/backgroundColor.js +34 -35
  132. package/src/plugins/dropdown/{formatBlock.js → blockStyle.js} +43 -37
  133. package/src/plugins/dropdown/font.js +50 -36
  134. package/src/plugins/dropdown/fontColor.js +34 -35
  135. package/src/plugins/dropdown/hr.js +55 -50
  136. package/src/plugins/dropdown/layout.js +20 -15
  137. package/src/plugins/dropdown/lineHeight.js +46 -30
  138. package/src/plugins/dropdown/list.js +32 -33
  139. package/src/plugins/dropdown/paragraphStyle.js +40 -34
  140. package/src/plugins/dropdown/table/index.js +915 -0
  141. package/src/plugins/dropdown/table/render/table.html.js +308 -0
  142. package/src/plugins/dropdown/table/render/table.menu.js +121 -0
  143. package/src/plugins/dropdown/table/services/table.cell.js +465 -0
  144. package/src/plugins/dropdown/table/services/table.clipboard.js +414 -0
  145. package/src/plugins/dropdown/table/services/table.grid.js +504 -0
  146. package/src/plugins/dropdown/table/services/table.resize.js +463 -0
  147. package/src/plugins/dropdown/table/services/table.selection.js +466 -0
  148. package/src/plugins/dropdown/table/services/table.style.js +844 -0
  149. package/src/plugins/dropdown/table/shared/table.constants.js +109 -0
  150. package/src/plugins/dropdown/table/shared/table.utils.js +219 -0
  151. package/src/plugins/dropdown/template.js +20 -15
  152. package/src/plugins/dropdown/textStyle.js +28 -22
  153. package/src/plugins/field/mention.js +54 -49
  154. package/src/plugins/index.js +5 -5
  155. package/src/plugins/input/fontSize.js +100 -97
  156. package/src/plugins/input/pageNavigator.js +13 -10
  157. package/src/plugins/modal/audio.js +208 -219
  158. package/src/plugins/modal/drawing.js +99 -104
  159. package/src/plugins/modal/embed.js +323 -312
  160. package/src/plugins/modal/image/index.js +942 -0
  161. package/src/plugins/modal/image/render/image.html.js +150 -0
  162. package/src/plugins/modal/image/services/image.size.js +198 -0
  163. package/src/plugins/modal/image/services/image.upload.js +216 -0
  164. package/src/plugins/modal/image/shared/image.constants.js +20 -0
  165. package/src/plugins/modal/link.js +74 -54
  166. package/src/plugins/modal/math.js +126 -119
  167. package/src/plugins/modal/video/index.js +858 -0
  168. package/src/plugins/modal/video/render/video.html.js +131 -0
  169. package/src/plugins/modal/video/services/video.size.js +281 -0
  170. package/src/plugins/modal/video/services/video.upload.js +92 -0
  171. package/src/plugins/popup/anchor.js +57 -49
  172. package/src/suneditor.js +73 -61
  173. package/src/themes/cobalt.css +155 -0
  174. package/src/themes/dark.css +143 -120
  175. package/src/typedef.js +214 -63
  176. package/types/assets/icons/defaultIcons.d.ts +8 -0
  177. package/types/assets/suneditor-contents.css.d.ts +1 -0
  178. package/types/assets/suneditor.css.d.ts +1 -0
  179. package/types/core/config/contextProvider.d.ts +148 -0
  180. package/types/core/config/eventManager.d.ts +68 -0
  181. package/types/core/config/instanceCheck.d.ts +33 -0
  182. package/types/core/config/optionProvider.d.ts +147 -0
  183. package/types/core/editor.d.ts +27 -586
  184. package/types/core/event/actions/index.d.ts +50 -0
  185. package/types/core/event/effects/common.registry.d.ts +56 -0
  186. package/types/core/event/effects/keydown.registry.d.ts +80 -0
  187. package/types/core/event/effects/ruleHelpers.d.ts +36 -0
  188. package/types/core/event/eventOrchestrator.d.ts +191 -0
  189. package/types/core/event/executor.d.ts +13 -0
  190. package/types/core/event/handlers/handler_toolbar.d.ts +38 -0
  191. package/types/core/event/handlers/handler_ww_clipboard.d.ts +36 -0
  192. package/types/core/event/handlers/handler_ww_dragDrop.d.ts +26 -0
  193. package/types/core/event/handlers/handler_ww_input.d.ts +38 -0
  194. package/types/core/event/handlers/handler_ww_key.d.ts +40 -0
  195. package/types/core/event/handlers/handler_ww_mouse.d.ts +47 -0
  196. package/types/core/event/ports.d.ts +256 -0
  197. package/types/core/event/reducers/keydown.reducer.d.ts +84 -0
  198. package/types/core/event/rules/keydown.rule.arrow.d.ts +19 -0
  199. package/types/core/event/rules/keydown.rule.backspace.d.ts +18 -0
  200. package/types/core/event/rules/keydown.rule.delete.d.ts +18 -0
  201. package/types/core/event/rules/keydown.rule.enter.d.ts +18 -0
  202. package/types/core/event/rules/keydown.rule.tab.d.ts +18 -0
  203. package/types/core/event/support/defaultLineManager.d.ts +22 -0
  204. package/types/core/event/support/selectionState.d.ts +29 -0
  205. package/types/core/kernel/coreKernel.d.ts +219 -0
  206. package/types/core/kernel/kernelInjector.d.ts +16 -0
  207. package/types/core/kernel/store.d.ts +170 -0
  208. package/types/core/logic/dom/char.d.ts +46 -0
  209. package/types/core/logic/dom/format.d.ts +234 -0
  210. package/types/core/logic/dom/html.d.ts +290 -0
  211. package/types/core/logic/dom/inline.d.ts +93 -0
  212. package/types/core/logic/dom/listFormat.d.ts +101 -0
  213. package/types/core/logic/dom/nodeTransform.d.ts +110 -0
  214. package/types/core/logic/dom/offset.d.ts +335 -0
  215. package/types/core/logic/dom/selection.d.ts +165 -0
  216. package/types/core/logic/panel/menu.d.ts +93 -0
  217. package/types/core/logic/panel/toolbar.d.ts +128 -0
  218. package/types/core/logic/panel/viewer.d.ts +89 -0
  219. package/types/core/logic/shell/_commandExecutor.d.ts +18 -0
  220. package/types/core/logic/shell/commandDispatcher.d.ts +65 -0
  221. package/types/core/logic/shell/component.d.ts +182 -0
  222. package/types/core/logic/shell/focusManager.d.ts +31 -0
  223. package/types/core/{base → logic/shell}/history.d.ts +13 -12
  224. package/types/core/logic/shell/pluginManager.d.ts +115 -0
  225. package/types/core/logic/shell/shortcuts.d.ts +131 -0
  226. package/types/core/logic/shell/ui.d.ts +261 -0
  227. package/types/core/schema/context.d.ts +104 -0
  228. package/types/core/schema/frameContext.d.ts +320 -0
  229. package/types/core/schema/options.d.ts +1241 -0
  230. package/types/core/section/constructor.d.ts +117 -652
  231. package/types/core/section/documentType.d.ts +43 -61
  232. package/types/events.d.ts +796 -65
  233. package/types/helper/clipboard.d.ts +5 -4
  234. package/types/helper/converter.d.ts +55 -43
  235. package/types/helper/dom/domCheck.d.ts +27 -19
  236. package/types/helper/dom/domQuery.d.ts +76 -57
  237. package/types/helper/dom/domUtils.d.ts +62 -39
  238. package/types/helper/dom/index.d.ts +87 -1
  239. package/types/helper/env.d.ts +16 -13
  240. package/types/helper/index.d.ts +8 -2
  241. package/types/helper/keyCodeMap.d.ts +24 -23
  242. package/types/helper/numbers.d.ts +4 -6
  243. package/types/helper/unicode.d.ts +4 -3
  244. package/types/hooks/base.d.ts +239 -0
  245. package/types/hooks/params.d.ts +65 -0
  246. package/types/index.d.ts +20 -117
  247. package/types/interfaces/contracts.d.ts +183 -0
  248. package/types/interfaces/index.d.ts +3 -0
  249. package/types/interfaces/plugins.d.ts +168 -0
  250. package/types/langs/_Lang.d.ts +2 -2
  251. package/types/langs/index.d.ts +2 -2
  252. package/types/modules/contract/Browser.d.ts +262 -0
  253. package/types/modules/contract/ColorPicker.d.ts +99 -0
  254. package/types/modules/contract/Controller.d.ts +204 -0
  255. package/types/modules/contract/Figure.d.ts +529 -0
  256. package/types/modules/{HueSlider.d.ts → contract/HueSlider.d.ts} +39 -28
  257. package/types/modules/contract/Modal.d.ts +62 -0
  258. package/types/modules/contract/index.d.ts +7 -0
  259. package/types/modules/manager/ApiManager.d.ts +106 -0
  260. package/types/modules/manager/FileManager.d.ts +124 -0
  261. package/types/modules/manager/index.d.ts +3 -0
  262. package/types/modules/ui/ModalAnchorEditor.d.ts +152 -0
  263. package/types/modules/ui/SelectMenu.d.ts +107 -0
  264. package/types/modules/{_DragHandle.d.ts → ui/_DragHandle.d.ts} +1 -0
  265. package/types/modules/ui/index.d.ts +4 -0
  266. package/types/plugins/browser/audioGallery.d.ts +33 -41
  267. package/types/plugins/browser/fileBrowser.d.ts +42 -50
  268. package/types/plugins/browser/fileGallery.d.ts +33 -41
  269. package/types/plugins/browser/imageGallery.d.ts +30 -37
  270. package/types/plugins/browser/videoGallery.d.ts +33 -41
  271. package/types/plugins/command/blockquote.d.ts +4 -21
  272. package/types/plugins/command/exportPDF.d.ts +23 -33
  273. package/types/plugins/command/fileUpload.d.ts +80 -100
  274. package/types/plugins/command/list_bulleted.d.ts +9 -35
  275. package/types/plugins/command/list_numbered.d.ts +9 -35
  276. package/types/plugins/dropdown/align.d.ts +23 -46
  277. package/types/plugins/dropdown/backgroundColor.d.ts +35 -53
  278. package/types/plugins/dropdown/blockStyle.d.ts +45 -0
  279. package/types/plugins/dropdown/font.d.ts +18 -41
  280. package/types/plugins/dropdown/fontColor.d.ts +35 -53
  281. package/types/plugins/dropdown/hr.d.ts +26 -52
  282. package/types/plugins/dropdown/layout.d.ts +19 -25
  283. package/types/plugins/dropdown/lineHeight.d.ts +21 -39
  284. package/types/plugins/dropdown/list.d.ts +6 -34
  285. package/types/plugins/dropdown/paragraphStyle.d.ts +34 -45
  286. package/types/plugins/dropdown/table/index.d.ts +158 -0
  287. package/types/plugins/dropdown/table/render/table.html.d.ts +71 -0
  288. package/types/plugins/dropdown/table/render/table.menu.d.ts +59 -0
  289. package/types/plugins/dropdown/table/services/table.cell.d.ts +76 -0
  290. package/types/plugins/dropdown/table/services/table.clipboard.d.ts +26 -0
  291. package/types/plugins/dropdown/table/services/table.grid.d.ts +77 -0
  292. package/types/plugins/dropdown/table/services/table.resize.d.ts +72 -0
  293. package/types/plugins/dropdown/table/services/table.selection.d.ts +59 -0
  294. package/types/plugins/dropdown/table/services/table.style.d.ts +162 -0
  295. package/types/plugins/dropdown/table/shared/table.constants.d.ts +134 -0
  296. package/types/plugins/dropdown/table/shared/table.utils.d.ts +91 -0
  297. package/types/plugins/dropdown/template.d.ts +19 -25
  298. package/types/plugins/dropdown/textStyle.d.ts +23 -30
  299. package/types/plugins/field/mention.d.ts +66 -72
  300. package/types/plugins/index.d.ts +41 -40
  301. package/types/plugins/input/fontSize.d.ts +57 -96
  302. package/types/plugins/input/pageNavigator.d.ts +5 -8
  303. package/types/plugins/modal/audio.d.ts +60 -153
  304. package/types/plugins/modal/drawing.d.ts +16 -118
  305. package/types/plugins/modal/embed.d.ts +46 -166
  306. package/types/plugins/modal/image/index.d.ts +281 -0
  307. package/types/plugins/modal/image/render/image.html.d.ts +45 -0
  308. package/types/plugins/modal/image/services/image.size.d.ts +55 -0
  309. package/types/plugins/modal/image/services/image.upload.d.ts +24 -0
  310. package/types/plugins/modal/image/shared/image.constants.d.ts +17 -0
  311. package/types/plugins/modal/link.d.ts +46 -66
  312. package/types/plugins/modal/math.d.ts +17 -86
  313. package/types/plugins/modal/{video.d.ts → video/index.d.ts} +89 -221
  314. package/types/plugins/modal/video/render/video.html.d.ts +37 -0
  315. package/types/plugins/modal/video/services/video.size.d.ts +74 -0
  316. package/types/plugins/modal/video/services/video.upload.d.ts +19 -0
  317. package/types/plugins/popup/anchor.d.ts +8 -38
  318. package/types/suneditor.d.ts +55 -24
  319. package/types/typedef.d.ts +344 -228
  320. package/CONTRIBUTING.md +0 -186
  321. package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
  322. package/src/core/base/eventHandlers/handler_ww_mouse.js +0 -194
  323. package/src/core/base/eventManager.js +0 -1523
  324. package/src/core/class/component.js +0 -856
  325. package/src/core/class/format.js +0 -3433
  326. package/src/core/class/menu.js +0 -346
  327. package/src/core/class/selection.js +0 -610
  328. package/src/core/class/shortcuts.js +0 -98
  329. package/src/core/class/toolbar.js +0 -431
  330. package/src/core/class/ui.js +0 -424
  331. package/src/core/class/viewer.js +0 -750
  332. package/src/core/section/actives.js +0 -266
  333. package/src/core/section/context.js +0 -102
  334. package/src/editorInjector/_classes.js +0 -36
  335. package/src/editorInjector/_core.js +0 -87
  336. package/src/editorInjector/index.js +0 -73
  337. package/src/modules/ApiManager.js +0 -191
  338. package/src/modules/Controller.js +0 -474
  339. package/src/modules/Modal.js +0 -346
  340. package/src/modules/index.js +0 -14
  341. package/src/plugins/dropdown/table.js +0 -4034
  342. package/src/plugins/modal/image.js +0 -1376
  343. package/src/plugins/modal/video.js +0 -1226
  344. package/types/core/base/eventHandlers/handler_toolbar.d.ts +0 -41
  345. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +0 -40
  346. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +0 -35
  347. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +0 -45
  348. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +0 -39
  349. package/types/core/base/eventManager.d.ts +0 -401
  350. package/types/core/class/char.d.ts +0 -61
  351. package/types/core/class/component.d.ts +0 -213
  352. package/types/core/class/format.d.ts +0 -623
  353. package/types/core/class/html.d.ts +0 -430
  354. package/types/core/class/menu.d.ts +0 -126
  355. package/types/core/class/nodeTransform.d.ts +0 -93
  356. package/types/core/class/offset.d.ts +0 -522
  357. package/types/core/class/selection.d.ts +0 -188
  358. package/types/core/class/shortcuts.d.ts +0 -142
  359. package/types/core/class/toolbar.d.ts +0 -189
  360. package/types/core/class/ui.d.ts +0 -164
  361. package/types/core/class/viewer.d.ts +0 -140
  362. package/types/core/section/actives.d.ts +0 -46
  363. package/types/core/section/context.d.ts +0 -45
  364. package/types/editorInjector/_classes.d.ts +0 -41
  365. package/types/editorInjector/_core.d.ts +0 -87
  366. package/types/editorInjector/index.d.ts +0 -69
  367. package/types/modules/ApiManager.d.ts +0 -125
  368. package/types/modules/Browser.d.ts +0 -326
  369. package/types/modules/ColorPicker.d.ts +0 -135
  370. package/types/modules/Controller.d.ts +0 -251
  371. package/types/modules/Figure.d.ts +0 -517
  372. package/types/modules/FileManager.d.ts +0 -202
  373. package/types/modules/Modal.d.ts +0 -111
  374. package/types/modules/ModalAnchorEditor.d.ts +0 -236
  375. package/types/modules/SelectMenu.d.ts +0 -194
  376. package/types/modules/index.d.ts +0 -26
  377. package/types/plugins/dropdown/formatBlock.d.ts +0 -55
  378. package/types/plugins/dropdown/table.d.ts +0 -627
  379. package/types/plugins/modal/image.d.ts +0 -451
  380. /package/{LICENSE → LICENSE.txt} +0 -0
@@ -1,3433 +0,0 @@
1
- /**
2
- * @fileoverview Format class
3
- */
4
-
5
- import CoreInjector from '../../editorInjector/_core';
6
- import { dom, unicode, numbers, converter } from '../../helper';
7
-
8
- /**
9
- * @typedef {Omit<Format & Partial<__se__EditorInjector>, 'format'>} FormatThis
10
- */
11
-
12
- /**
13
- * @typedef {Object} NodeStyleContainerType
14
- * @property {?Node=} ancestor
15
- * @property {?number=} offset
16
- * @property {?Node=} container
17
- * @property {?Node=} endContainer
18
- */
19
-
20
- /**
21
- * @constructor
22
- * @this {FormatThis}
23
- * @description Classes related to editor formats such as line creation, line retrieval from selected range, etc.
24
- * @param {__se__EditorCore} editor - The root editor instance
25
- */
26
- function Format(editor) {
27
- CoreInjector.call(this, editor);
28
-
29
- // members
30
- this._listCamel = this.options.get('__listCommonStyle');
31
- this._listKebab = converter.camelToKebabCase(this.options.get('__listCommonStyle'));
32
- this._formatLineCheck = this.options.get('formatLine').reg;
33
- this._formatBrLineCheck = this.options.get('formatBrLine').reg;
34
- this._formatBlockCheck = this.options.get('formatBlock').reg;
35
- this._formatClosureBlockCheck = this.options.get('formatClosureBlock').reg;
36
- this._formatClosureBrLineCheck = this.options.get('formatClosureBrLine').reg;
37
- this._textStyleTagsCheck = new RegExp('^(' + this.options.get('textStyleTags') + ')$', 'i');
38
-
39
- this._brLineBreak = null;
40
- this.__resetBrLineBreak(this.options.get('defaultLineBreakFormat'));
41
- }
42
-
43
- Format.prototype = {
44
- /**
45
- * @this {FormatThis}
46
- * @description Replace the line tag of the current selection.
47
- * @param {Node} element Line element (P, DIV..)
48
- */
49
- setLine(element) {
50
- if (!this.isLine(element)) {
51
- throw new Error('[SUNEDITOR.format.setLine.fail] The "element" must satisfy "format.isLine()".');
52
- }
53
-
54
- const info = this._lineWork();
55
- const lines = info.lines;
56
- const className = element.className;
57
- const value = element.nodeName;
58
- let first = info.firstNode;
59
- let last = info.lastNode;
60
-
61
- for (let i = 0, len = lines.length, node, newFormat; i < len; i++) {
62
- node = lines[i];
63
-
64
- if ((node.nodeName !== value || (node.className.match(/(\s|^)__se__format__[^\s]+/) || [''])[0].trim() !== className) && !this.component.is(node)) {
65
- newFormat = /** @type {HTMLElement} */ (element.cloneNode(false));
66
- dom.utils.copyFormatAttributes(newFormat, node);
67
- newFormat.innerHTML = node.innerHTML;
68
-
69
- node.parentNode.replaceChild(newFormat, node);
70
- }
71
-
72
- if (i === 0) first = newFormat || node;
73
- if (i === len - 1) last = newFormat || node;
74
- newFormat = null;
75
- }
76
-
77
- this.selection.setRange(dom.query.getNodeFromPath(info.firstPath, first), info.startOffset, dom.query.getNodeFromPath(info.lastPath, last), info.endOffset);
78
- this.history.push(false);
79
-
80
- // document type
81
- if (this.editor.frameContext.has('documentType-use-header')) {
82
- this.editor.frameContext.get('documentType').reHeader();
83
- }
84
- },
85
-
86
- /**
87
- * @this {FormatThis}
88
- * @description If a parent node that contains an argument node finds a format node (format.isLine), it returns that node.
89
- * @param {Node} node Reference node.
90
- * @param {?(current: Node) => boolean=} validation Additional validation function.
91
- * @returns {HTMLElement|null}
92
- */
93
- getLine(node, validation) {
94
- if (!node) return null;
95
- if (!validation) {
96
- validation = function () {
97
- return true;
98
- };
99
- }
100
-
101
- while (node) {
102
- if (dom.check.isWysiwygFrame(node)) return null;
103
- if (this.isBlock(node)) return /** @type {HTMLElement} */ (node.firstElementChild);
104
- if (this.isLine(node) && validation(node)) return /** @type {HTMLElement} */ (node);
105
-
106
- node = node.parentNode;
107
- }
108
-
109
- return null;
110
- },
111
-
112
- /**
113
- * @this {FormatThis}
114
- * @description Replace the br-line tag of the current selection.
115
- * @param {Node} element BR-Line element (PRE..)
116
- */
117
- setBrLine(element) {
118
- if (!this.isBrLine(element)) {
119
- throw new Error('[SUNEDITOR.format.setBrLine.fail] The "element" must satisfy "format.isBrLine()".');
120
- }
121
-
122
- const lines = this._lineWork().lines;
123
- const len = lines.length - 1;
124
- let parentNode = lines[len].parentNode;
125
- let freeElement = /** @type {HTMLElement} */ (element.cloneNode(false));
126
- const focusElement = freeElement;
127
-
128
- for (let i = len, f, html, before, next, inner, isComp, first = true; i >= 0; i--) {
129
- f = lines[i];
130
- if (f === (!lines[i + 1] ? null : lines[i + 1].parentNode)) continue;
131
-
132
- isComp = this.component.is(f);
133
- html = isComp ? '' : f.innerHTML.replace(/(?!>)\s+(?=<)|\n/g, ' ');
134
- before = dom.query.getParentElement(f, (current) => current.parentNode === parentNode);
135
-
136
- if (parentNode !== f.parentNode || isComp) {
137
- if (this.isLine(parentNode)) {
138
- parentNode.parentNode.insertBefore(freeElement, parentNode.nextSibling);
139
- parentNode = parentNode.parentNode;
140
- } else {
141
- parentNode.insertBefore(freeElement, before ? before.nextSibling : null);
142
- parentNode = f.parentNode;
143
- }
144
-
145
- next = /** @type {HTMLElement} */ (freeElement.nextSibling);
146
- if (next && freeElement.nodeName === next.nodeName && dom.check.isSameAttributes(freeElement, next)) {
147
- freeElement.innerHTML += '<BR>' + next.innerHTML;
148
- dom.utils.removeItem(next);
149
- }
150
-
151
- freeElement = /** @type {HTMLElement} */ (element.cloneNode(false));
152
- first = true;
153
- }
154
-
155
- inner = freeElement.innerHTML;
156
- freeElement.innerHTML = (first || !html || !inner || /<br>$/i.test(html) ? html : html + '<BR>') + inner;
157
-
158
- if (i === 0) {
159
- parentNode.insertBefore(freeElement, f);
160
- next = /** @type {HTMLElement} */ (f.nextSibling);
161
- if (next && freeElement.nodeName === next.nodeName && dom.check.isSameAttributes(freeElement, next)) {
162
- freeElement.innerHTML += '<BR>' + next.innerHTML;
163
- dom.utils.removeItem(next);
164
- }
165
-
166
- const prev = /** @type {HTMLElement} */ (freeElement.previousSibling);
167
- if (prev && freeElement.nodeName === prev.nodeName && dom.check.isSameAttributes(freeElement, prev)) {
168
- prev.innerHTML += '<BR>' + freeElement.innerHTML;
169
- dom.utils.removeItem(freeElement);
170
- }
171
- }
172
-
173
- if (!isComp) dom.utils.removeItem(f);
174
- if (html) first = false;
175
- }
176
-
177
- this.selection.setRange(focusElement, 0, focusElement, 0);
178
- this.history.push(false);
179
- },
180
-
181
- /**
182
- * @this {FormatThis}
183
- * @description If a parent node that contains an argument node finds a "brLine" (format.isBrLine), it returns that node.
184
- * @param {Node} element Reference node.
185
- * @param {?(current: Node) => boolean=} validation Additional validation function.
186
- * @returns {HTMLBRElement|null}
187
- */
188
- getBrLine(element, validation) {
189
- if (!element) return null;
190
- if (!validation) {
191
- validation = function () {
192
- return true;
193
- };
194
- }
195
-
196
- while (element) {
197
- if (dom.check.isWysiwygFrame(element)) return null;
198
- if (this.isBrLine(element) && validation(element)) return /** @type {HTMLBRElement} */ (element);
199
-
200
- element = element.parentNode;
201
- }
202
-
203
- return null;
204
- },
205
-
206
- /**
207
- * @this {FormatThis}
208
- * @description Append "line" element to sibling node of argument element.
209
- * - If the "lineNode" argument value is present, the tag of that argument value is inserted,
210
- * - If not, the currently selected format tag is inserted.
211
- * @param {Node} element Insert as siblings of that element
212
- * @param {?string|Node=} lineNode Node name or node obejct to be inserted
213
- * @returns {HTMLElement}
214
- */
215
- addLine(element, lineNode) {
216
- if (!element || !element.parentNode) return null;
217
-
218
- const currentFormatEl = this.getLine(this.selection.getNode(), null);
219
- let oFormat = null;
220
- if (!this.isBrLine(element) && this.isBrLine(currentFormatEl || element.parentNode) && !this.component.is(element)) {
221
- oFormat = dom.utils.createElement('BR');
222
- } else {
223
- const oFormatName = lineNode ? (typeof lineNode === 'string' ? lineNode : lineNode.nodeName) : this.isLineOnly(currentFormatEl) ? currentFormatEl.nodeName : this.options.get('defaultLine');
224
- oFormat = dom.utils.createElement(oFormatName, null, '<br>');
225
- if ((lineNode && typeof lineNode !== 'string') || (!lineNode && this.isLine(currentFormatEl))) {
226
- dom.utils.copyTagAttributes(oFormat, /** @type {Node} */ (lineNode || currentFormatEl), ['id']);
227
- }
228
- }
229
-
230
- if (dom.check.isTableCell(element)) element.insertBefore(oFormat, element.nextElementSibling);
231
- else element.parentNode.insertBefore(oFormat, /** @type {HTMLElement} */ (element).nextElementSibling);
232
-
233
- return oFormat;
234
- },
235
-
236
- /**
237
- * @this {FormatThis}
238
- * @description If a parent node that contains an argument node finds a format node (format.isBlock), it returns that node.
239
- * @param {Node} element Reference node.
240
- * @param {?(current: Node) => boolean=} validation Additional validation function.
241
- * @returns {HTMLElement|null}
242
- */
243
- getBlock(element, validation) {
244
- if (!element) return null;
245
- if (!validation) {
246
- validation = function () {
247
- return true;
248
- };
249
- }
250
-
251
- while (element) {
252
- if (dom.check.isWysiwygFrame(element)) return null;
253
- if (this.isBlock(element) && !/^(THEAD|TBODY|TR)$/i.test(element.nodeName) && validation(element)) return element;
254
- element = element.parentNode;
255
- }
256
-
257
- return null;
258
- },
259
-
260
- /**
261
- * @this {FormatThis}
262
- * @description Appended all selected "line" element to the argument element("block") and insert
263
- * @param {Node} blockElement Element of wrap the arguments (BLOCKQUOTE...)
264
- */
265
- applyBlock(blockElement) {
266
- this.selection.getRangeAndAddLine(this.selection.getRange(), null);
267
- const rangeLines = /** @type {Element[]} */ (this.getLinesAndComponents(false));
268
- if (!rangeLines || rangeLines.length === 0) return;
269
-
270
- linesLoop: for (let i = 0, len = rangeLines.length, line, nested, fEl, lEl, f, l; i < len; i++) {
271
- line = rangeLines[i];
272
- if (!dom.check.isListCell(line)) continue;
273
-
274
- nested = line.lastElementChild;
275
- if (nested && dom.check.isListCell(line.nextElementSibling) && rangeLines.includes(line.nextElementSibling)) {
276
- lEl = nested.lastElementChild;
277
- if (rangeLines.includes(lEl)) {
278
- let list = null;
279
- while ((list = lEl.lastElementChild)) {
280
- if (dom.check.isList(list)) {
281
- if (rangeLines.includes(list.lastElementChild)) {
282
- lEl = list.lastElementChild;
283
- } else {
284
- continue linesLoop;
285
- }
286
- }
287
- }
288
-
289
- fEl = nested.firstElementChild;
290
- f = rangeLines.indexOf(fEl);
291
- l = rangeLines.indexOf(lEl);
292
- rangeLines.splice(f, l - f + 1);
293
- len = rangeLines.length;
294
- continue;
295
- }
296
- }
297
- }
298
-
299
- const last = rangeLines[rangeLines.length - 1];
300
- let standTag, beforeTag, pElement;
301
-
302
- if (this.isBlock(last) || this.isLine(last)) {
303
- standTag = last;
304
- } else {
305
- standTag = this.getBlock(last, null) || this.getLine(last, null);
306
- }
307
-
308
- if (dom.check.isTableCell(standTag)) {
309
- beforeTag = null;
310
- pElement = standTag;
311
- } else {
312
- beforeTag = standTag.nextSibling;
313
- pElement = standTag.parentNode;
314
- }
315
-
316
- const block = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
317
- let parentDepth = dom.query.getNodeDepth(standTag);
318
- let listParent = null;
319
- const lineArr = [];
320
- const removeItems = (parent, origin, before) => {
321
- let cc = null;
322
- if (parent !== origin && !dom.check.isTableElements(origin)) {
323
- if (origin && dom.query.getNodeDepth(parent) === dom.query.getNodeDepth(origin)) return before;
324
- cc = this.nodeTransform.removeAllParents(origin, null, parent);
325
- }
326
-
327
- return cc ? cc.ec : before;
328
- };
329
-
330
- for (let i = 0, len = rangeLines.length, line, originParent, depth, before, nextLine, nextList, nested; i < len; i++) {
331
- line = rangeLines[i];
332
- originParent = line.parentNode;
333
- if (!originParent || block.contains(originParent)) continue;
334
-
335
- depth = dom.query.getNodeDepth(line);
336
-
337
- if (dom.check.isList(originParent)) {
338
- if (listParent === null) {
339
- if (nextList) {
340
- listParent = nextList;
341
- nested = true;
342
- nextList = null;
343
- } else {
344
- listParent = originParent.cloneNode(false);
345
- }
346
- }
347
-
348
- lineArr.push(line);
349
- nextLine = rangeLines[i + 1];
350
-
351
- if (i === len - 1 || nextLine?.parentNode !== originParent) {
352
- // nested list
353
- if (line.contains(nextLine?.parentNode)) {
354
- nextList = nextLine.parentNode.cloneNode(false);
355
- }
356
-
357
- let list = originParent.parentNode,
358
- p;
359
- while (dom.check.isList(list)) {
360
- p = dom.utils.createElement(list.nodeName);
361
- p.appendChild(listParent);
362
- listParent = p;
363
- list = list.parentNode;
364
- }
365
-
366
- const edge = this.removeBlock(originParent, { selectedFormats: lineArr, newBlockElement: null, shouldDelete: true, skipHistory: true });
367
-
368
- if (parentDepth >= depth) {
369
- parentDepth = depth;
370
- pElement = edge.cc;
371
- beforeTag = removeItems(pElement, originParent, edge.ec);
372
- if (beforeTag) pElement = beforeTag.parentNode;
373
- } else if (pElement === edge.cc) {
374
- beforeTag = edge.ec;
375
- }
376
-
377
- if (pElement !== edge.cc) {
378
- before = removeItems(pElement, edge.cc, before);
379
- if (before !== undefined) beforeTag = before;
380
- else beforeTag = edge.cc;
381
- }
382
-
383
- for (let c = 0, cLen = edge.removeArray.length; c < cLen; c++) {
384
- listParent.appendChild(edge.removeArray[c]);
385
- }
386
-
387
- if (!nested) block.appendChild(listParent);
388
- if (nextList) edge.removeArray[edge.removeArray.length - 1].appendChild(nextList);
389
- listParent = null;
390
- nested = false;
391
- }
392
- } else {
393
- if (parentDepth >= depth) {
394
- parentDepth = depth;
395
- pElement = originParent;
396
- beforeTag = line.nextSibling;
397
- }
398
-
399
- block.appendChild(line);
400
-
401
- if (pElement !== originParent) {
402
- before = removeItems(pElement, originParent);
403
- if (before !== undefined) beforeTag = before;
404
- }
405
- }
406
- }
407
-
408
- this.editor.effectNode = null;
409
- this.nodeTransform.mergeSameTags(block, null, false);
410
- this.nodeTransform.mergeNestedTags(block, (current) => dom.check.isList(current));
411
-
412
- // Nested list
413
- if (beforeTag && dom.query.getNodeDepth(beforeTag) > 0 && (dom.check.isList(beforeTag.parentNode) || dom.check.isList(beforeTag.parentNode.parentNode))) {
414
- const depthFormat = dom.query.getParentElement(beforeTag, (current) => this.isBlock(current) && !dom.check.isList(current));
415
- const splitRange = this.nodeTransform.split(beforeTag, null, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
416
- splitRange.parentNode.insertBefore(block, splitRange);
417
- } else {
418
- // basic
419
- pElement.insertBefore(block, beforeTag);
420
- removeItems(block, beforeTag);
421
- }
422
-
423
- const edge = dom.query.getEdgeChildNodes(block.firstElementChild, block.lastElementChild);
424
- if (rangeLines.length > 1) {
425
- this.selection.setRange(edge.sc, 0, edge.ec, edge.ec.textContent.length);
426
- } else {
427
- this.selection.setRange(edge.ec, edge.ec.textContent.length, edge.ec, edge.ec.textContent.length);
428
- }
429
-
430
- this.history.push(false);
431
- },
432
-
433
- /**
434
- * @this {FormatThis}
435
- * @description The elements of the "selectedFormats" array are detached from the "blockElement" element. ("LI" tags are converted to "P" tags)
436
- * - When "selectedFormats" is null, all elements are detached and return {cc: parentNode, sc: nextSibling, ec: previousSibling, removeArray: [Array of removed elements]}.
437
- * @param {Node} blockElement "block" element (PRE, BLOCKQUOTE, OL, UL...)
438
- * @param {Object} [options] Options
439
- * @param {Array<Node>} [options.selectedFormats=null] Array of "line" elements (P, DIV, LI...) to remove.
440
- * - If null, Applies to all elements and return {cc: parentNode, sc: nextSibling, ec: previousSibling}
441
- * @param {Node} [options.newBlockElement=null] The node(blockElement) to replace the currently wrapped node.
442
- * @param {boolean} [options.shouldDelete=false] If true, deleted without detached.
443
- * @param {boolean} [options.skipHistory=false] When true, it does not update the history stack and the selection object and return EdgeNodes (dom-query-GetEdgeChildNodes)
444
- * @returns {{cc: Node, sc: Node, so: number, ec: Node, eo: number, removeArray: Array<Node>|null}} Node information after deletion
445
- * - cc: Common parent container node
446
- * - sc: Start container node
447
- * - so: Start offset
448
- * - ec: End container node
449
- * - eo: End offset
450
- * - removeArray: Array of removed elements
451
- */
452
- removeBlock(blockElement, { selectedFormats, newBlockElement, shouldDelete, skipHistory } = {}) {
453
- const range = this.selection.getRange();
454
- let so = range.startOffset;
455
- let eo = range.endOffset;
456
-
457
- let children = dom.query.getListChildNodes(blockElement, (current) => current.parentNode === blockElement);
458
- let parent = blockElement.parentNode;
459
- let firstNode = null;
460
- let lastNode = null;
461
- let rangeEl = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
462
-
463
- const removeArray = [];
464
- const newList = dom.check.isList(newBlockElement);
465
- let insertedNew = false;
466
- let reset = false;
467
- let moveComplete = false;
468
-
469
- const appendNode = (parentEl, insNode, sibling, originNode) => {
470
- if (dom.check.isZeroWidth(insNode)) {
471
- insNode.innerHTML = unicode.zeroWidthSpace;
472
- so = eo = 1;
473
- }
474
-
475
- if (insNode.nodeType === 3) {
476
- parentEl.insertBefore(insNode, sibling);
477
- return insNode;
478
- }
479
-
480
- const insChildren = (moveComplete ? insNode : originNode).childNodes;
481
- let format = insNode.cloneNode(false);
482
- let first = null;
483
- let c = null;
484
-
485
- while (insChildren[0]) {
486
- c = insChildren[0];
487
- if (this._notTextNode(c) && !dom.check.isBreak(c) && !dom.check.isListCell(format)) {
488
- if (format.childNodes.length > 0) {
489
- if (!first) first = format;
490
- parentEl.insertBefore(format, sibling);
491
- format = insNode.cloneNode(false);
492
- }
493
- parentEl.insertBefore(c, sibling);
494
- if (!first) first = c;
495
- } else {
496
- format.appendChild(c);
497
- }
498
- }
499
-
500
- if (format.childNodes.length > 0) {
501
- if (dom.check.isListCell(parentEl) && dom.check.isListCell(format) && dom.check.isList(sibling)) {
502
- if (newList) {
503
- first = sibling;
504
- while (sibling) {
505
- format.appendChild(sibling);
506
- sibling = sibling.nextSibling;
507
- }
508
- parentEl.parentNode.insertBefore(format, parentEl.nextElementSibling);
509
- } else {
510
- const originNext = originNode.nextElementSibling;
511
- const detachRange = this._removeNestedList(originNode, false);
512
- if (blockElement !== detachRange || originNext !== originNode.nextElementSibling) {
513
- const fChildren = format.childNodes;
514
- while (fChildren[0]) {
515
- originNode.appendChild(fChildren[0]);
516
- }
517
-
518
- blockElement = detachRange;
519
- reset = true;
520
- }
521
- }
522
- } else {
523
- parentEl.insertBefore(format, sibling);
524
- }
525
-
526
- if (!first) first = format;
527
- }
528
-
529
- return first;
530
- };
531
-
532
- // detach loop
533
- for (let i = 0, len = children.length, insNode, lineIndex, next; i < len; i++) {
534
- insNode = children[i];
535
- if (insNode.nodeType === 3 && dom.check.isList(rangeEl)) continue;
536
-
537
- moveComplete = false;
538
- if (shouldDelete && i === 0) {
539
- if (!selectedFormats || selectedFormats.length === len || selectedFormats[0] === insNode) {
540
- firstNode = blockElement.previousSibling;
541
- } else {
542
- firstNode = rangeEl;
543
- }
544
- }
545
-
546
- if (selectedFormats) lineIndex = selectedFormats.indexOf(insNode);
547
- if (selectedFormats && lineIndex === -1) {
548
- if (!rangeEl) rangeEl = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
549
- rangeEl.appendChild(insNode);
550
- } else {
551
- if (selectedFormats) next = selectedFormats[lineIndex + 1];
552
- if (rangeEl && rangeEl.children.length > 0) {
553
- parent.insertBefore(rangeEl, blockElement);
554
- rangeEl = null;
555
- }
556
-
557
- if (!newList && dom.check.isListCell(insNode)) {
558
- if (next && dom.query.getNodeDepth(insNode) !== dom.query.getNodeDepth(next) && (dom.check.isListCell(parent) || dom.utils.arrayFind(insNode.children, dom.check.isList))) {
559
- const insNext = insNode.nextElementSibling;
560
- const detachRange = this._removeNestedList(insNode, false);
561
- if (blockElement !== detachRange || insNext !== insNode.nextElementSibling) {
562
- blockElement = detachRange;
563
- reset = true;
564
- }
565
- } else {
566
- const inner = insNode;
567
- insNode = dom.utils.createElement(
568
- shouldDelete
569
- ? inner.nodeName
570
- : dom.check.isList(blockElement.parentNode) || dom.check.isListCell(blockElement.parentNode)
571
- ? 'LI'
572
- : dom.check.isTableCell(blockElement.parentNode)
573
- ? 'DIV'
574
- : this.options.get('defaultLine')
575
- );
576
- const isCell = dom.check.isListCell(insNode);
577
- const innerChildren = inner.childNodes;
578
- while (innerChildren[0]) {
579
- if (dom.check.isList(innerChildren[0]) && !isCell) break;
580
- insNode.appendChild(innerChildren[0]);
581
- }
582
- dom.utils.copyFormatAttributes(insNode, inner);
583
- moveComplete = true;
584
- }
585
- } else {
586
- insNode = insNode.cloneNode(false);
587
- }
588
-
589
- if (!reset) {
590
- if (!shouldDelete) {
591
- if (newBlockElement) {
592
- if (!insertedNew) {
593
- parent.insertBefore(newBlockElement, blockElement);
594
- insertedNew = true;
595
- }
596
- insNode = appendNode(newBlockElement, insNode, null, children[i]);
597
- } else {
598
- insNode = appendNode(parent, insNode, blockElement, children[i]);
599
- }
600
-
601
- if (!reset) {
602
- if (selectedFormats) {
603
- lastNode = insNode;
604
- if (!firstNode) {
605
- firstNode = insNode;
606
- }
607
- } else if (!firstNode) {
608
- firstNode = lastNode = insNode;
609
- }
610
- }
611
- } else {
612
- removeArray.push(insNode);
613
- dom.utils.removeItem(children[i]);
614
- }
615
-
616
- if (reset) {
617
- reset = moveComplete = false;
618
- children = dom.query.getListChildNodes(blockElement, (current) => current.parentNode === blockElement);
619
- rangeEl = /** @type {HTMLElement} */ (blockElement.cloneNode(false));
620
- parent = blockElement.parentNode;
621
- i = -1;
622
- len = children.length;
623
- continue;
624
- }
625
- }
626
- }
627
- }
628
-
629
- const rangeParent = blockElement.parentNode;
630
- let rangeRight = blockElement.nextSibling;
631
- if (rangeEl?.children.length > 0) {
632
- rangeParent.insertBefore(rangeEl, rangeRight);
633
- }
634
-
635
- if (newBlockElement) firstNode = newBlockElement.previousSibling;
636
- else if (!firstNode) firstNode = blockElement.previousSibling;
637
- rangeRight = blockElement.nextSibling !== rangeEl ? blockElement.nextSibling : rangeEl ? rangeEl.nextSibling : null;
638
-
639
- if (/** @type {HTMLElement} */ (blockElement).children.length === 0 || blockElement.textContent.length === 0) {
640
- dom.utils.removeItem(blockElement);
641
- } else {
642
- this.nodeTransform.removeEmptyNode(blockElement, null, false);
643
- }
644
-
645
- let edge = null;
646
- if (shouldDelete) {
647
- edge = {
648
- cc: rangeParent,
649
- sc: firstNode,
650
- so: so,
651
- ec: rangeRight,
652
- eo: eo,
653
- removeArray: removeArray
654
- };
655
- } else {
656
- if (!firstNode) firstNode = lastNode;
657
- if (!lastNode) lastNode = firstNode;
658
- const childEdge = dom.query.getEdgeChildNodes(firstNode, lastNode.parentNode ? firstNode : lastNode);
659
- edge = {
660
- cc: (childEdge.sc || childEdge.ec).parentNode,
661
- sc: childEdge.sc,
662
- so: so,
663
- ec: childEdge.ec,
664
- eo: eo,
665
- removeArray: null
666
- };
667
- }
668
-
669
- this.editor.effectNode = null;
670
- if (skipHistory) return edge;
671
-
672
- if (!shouldDelete && edge) {
673
- if (!selectedFormats) {
674
- this.selection.setRange(edge.sc, 0, edge.sc, 0);
675
- } else {
676
- this.selection.setRange(edge.sc, so, edge.ec, eo);
677
- }
678
- }
679
-
680
- this.history.push(false);
681
- },
682
-
683
- /**
684
- * @this {FormatThis}
685
- * @description Append all selected "line" element to the list and insert.
686
- * @param {string} type List type. (ol | ul):[listStyleType]
687
- * @param {Array<Node>} selectedCells "line" elements or list cells.
688
- * @param {boolean} nested If true, indenting existing list cells.
689
- */
690
- applyList(type, selectedCells, nested) {
691
- const listTag = (type.split(':')[0] || 'ol').toUpperCase();
692
- const listStyle = type.split(':')[1] || '';
693
-
694
- let range = this.selection.getRange();
695
- let selectedFormats = /** @type {Array<HTMLElement>} */ (!selectedCells ? this.getLinesAndComponents(false) : selectedCells);
696
-
697
- if (selectedFormats.length === 0) {
698
- if (selectedCells) return;
699
- range = this.selection.getRangeAndAddLine(range, null);
700
- selectedFormats = this.getLinesAndComponents(false);
701
- if (selectedFormats.length === 0) return;
702
- }
703
-
704
- dom.query.sortNodeByDepth(selectedFormats, true);
705
-
706
- // merge
707
- const firstSel = selectedFormats[0];
708
- const lastSel = selectedFormats[selectedFormats.length - 1];
709
- let topEl = (dom.check.isListCell(firstSel) || this.component.is(firstSel)) && !firstSel.previousElementSibling ? firstSel.parentElement.previousElementSibling : firstSel.previousElementSibling;
710
- let bottomEl = (dom.check.isListCell(lastSel) || this.component.is(lastSel)) && !lastSel.nextElementSibling ? lastSel.parentElement.nextElementSibling : lastSel.nextElementSibling;
711
-
712
- const isCollapsed = range.collapsed;
713
- const originRange = {
714
- sc: range.startContainer,
715
- so: range.startContainer === range.endContainer && dom.check.isZeroWidth(range.startContainer) && range.startOffset === 0 && range.endOffset === 1 ? range.endOffset : range.startOffset,
716
- ec: range.endContainer,
717
- eo: range.endOffset
718
- };
719
- let afterRange = null;
720
- let isRemove = true;
721
-
722
- for (let i = 0, len = selectedFormats.length; i < len; i++) {
723
- if (!dom.check.isList(this.getBlock(selectedFormats[i], (current) => this.getBlock(current) && current !== selectedFormats[i]))) {
724
- isRemove = false;
725
- break;
726
- }
727
- }
728
-
729
- if (isRemove && (!topEl || firstSel.tagName !== topEl.tagName || listTag !== topEl.tagName.toUpperCase()) && (!bottomEl || lastSel.tagName !== bottomEl.tagName || listTag !== bottomEl.tagName.toUpperCase())) {
730
- if (nested) {
731
- for (let i = 0, len = selectedFormats.length; i < len; i++) {
732
- for (let j = i - 1; j >= 0; j--) {
733
- if (selectedFormats[j].contains(selectedFormats[i])) {
734
- selectedFormats.splice(i, 1);
735
- i--;
736
- len--;
737
- break;
738
- }
739
- }
740
- }
741
- }
742
-
743
- const currentFormat = this.getBlock(firstSel);
744
- const cancel = currentFormat?.tagName === listTag;
745
- let rangeArr, tempList;
746
- const passComponent = (current) => {
747
- return !this.component.is(current);
748
- };
749
-
750
- if (!cancel) {
751
- tempList = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
752
- }
753
-
754
- for (let i = 0, len = selectedFormats.length, r, o; i < len; i++) {
755
- o = this.getBlock(selectedFormats[i], passComponent);
756
- if (!o || !dom.check.isList(o)) continue;
757
-
758
- if (!r) {
759
- r = o;
760
- rangeArr = {
761
- r: r,
762
- f: [dom.query.getParentElement(selectedFormats[i], 'LI')]
763
- };
764
- } else {
765
- if (r !== o) {
766
- if (nested && dom.check.isListCell(o.parentNode)) {
767
- this._detachNested(rangeArr.f);
768
- } else {
769
- afterRange = this.removeBlock(rangeArr.f[0].parentElement, { selectedFormats: rangeArr.f, newBlockElement: tempList, shouldDelete: false, skipHistory: true });
770
- }
771
-
772
- o = selectedFormats[i].parentNode;
773
- if (!cancel) {
774
- tempList = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
775
- }
776
-
777
- r = o;
778
- rangeArr = {
779
- r: r,
780
- f: [dom.query.getParentElement(selectedFormats[i], 'LI')]
781
- };
782
- } else {
783
- rangeArr.f.push(dom.query.getParentElement(selectedFormats[i], 'LI'));
784
- }
785
- }
786
-
787
- if (i === len - 1) {
788
- if (nested && dom.check.isListCell(o.parentNode)) {
789
- this._detachNested(rangeArr.f);
790
- } else {
791
- afterRange = this.removeBlock(rangeArr.f[0].parentElement, { selectedFormats: rangeArr.f, newBlockElement: tempList, shouldDelete: false, skipHistory: true });
792
- }
793
- }
794
- }
795
- } else {
796
- const topElParent = topEl ? topEl.parentNode : topEl;
797
- const bottomElParent = bottomEl ? bottomEl.parentNode : bottomEl;
798
- topEl = /** @type {HTMLElement} */ (topElParent && !dom.check.isWysiwygFrame(topElParent) && topElParent.nodeName === listTag ? topElParent : topEl);
799
- bottomEl = /** @type {HTMLElement} */ (bottomElParent && !dom.check.isWysiwygFrame(bottomElParent) && bottomElParent.nodeName === listTag ? bottomElParent : bottomEl);
800
-
801
- const mergeTop = topEl?.tagName === listTag;
802
- const mergeBottom = bottomEl?.tagName === listTag;
803
-
804
- let list = mergeTop ? topEl : dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
805
- let firstList = null;
806
- let topNumber = null;
807
- // let lastList = null;
808
- // let bottomNumber = null;
809
-
810
- const passComponent = (current) => {
811
- return !this.component.is(current) && !dom.check.isList(current);
812
- };
813
-
814
- for (let i = 0, len = selectedFormats.length, newCell, fTag, isCell, next, originParent, nextParent, parentTag, siblingTag, rangeTag; i < len; i++) {
815
- fTag = selectedFormats[i];
816
- if (fTag.childNodes.length === 0 && !this._isIgnoreNodeChange(fTag)) {
817
- dom.utils.removeItem(fTag);
818
- continue;
819
- }
820
- next = selectedFormats[i + 1];
821
- originParent = fTag.parentNode;
822
- nextParent = next ? next.parentNode : null;
823
- isCell = dom.check.isListCell(fTag);
824
- rangeTag = this.isBlock(originParent) ? originParent : null;
825
- parentTag = isCell && !dom.check.isWysiwygFrame(originParent) ? originParent.parentNode : originParent;
826
- siblingTag = isCell && !dom.check.isWysiwygFrame(originParent) ? (!next || dom.check.isListCell(parentTag) ? originParent : originParent.nextSibling) : fTag.nextSibling;
827
-
828
- newCell = dom.utils.createElement('LI');
829
- dom.utils.copyFormatAttributes(newCell, fTag);
830
- if (this.component.is(fTag)) {
831
- const isHR = /^HR$/i.test(fTag.nodeName);
832
- if (!isHR) newCell.innerHTML = '<br>';
833
- newCell.innerHTML += fTag.outerHTML;
834
- if (isHR) newCell.innerHTML += '<br>';
835
- } else {
836
- const fChildren = fTag.childNodes;
837
- while (fChildren[0]) {
838
- newCell.appendChild(fChildren[0]);
839
- }
840
- }
841
- list.appendChild(newCell);
842
-
843
- // if (!next) lastList = list;
844
- if (!next || parentTag !== nextParent || this.isBlock(siblingTag)) {
845
- if (!firstList) firstList = list;
846
- if ((!mergeTop || !next || parentTag !== nextParent) && !(next && dom.check.isList(nextParent) && nextParent === originParent)) {
847
- if (list.parentNode !== parentTag) parentTag.insertBefore(list, siblingTag);
848
- }
849
- }
850
-
851
- dom.utils.removeItem(fTag);
852
- if (mergeTop && topNumber === null) topNumber = list.children.length - 1;
853
- if (
854
- next &&
855
- (this.getBlock(nextParent, passComponent) !== this.getBlock(originParent, passComponent) ||
856
- (dom.check.isList(nextParent) && dom.check.isList(originParent) && dom.query.getNodeDepth(nextParent) !== dom.query.getNodeDepth(originParent)))
857
- ) {
858
- list = dom.utils.createElement(listTag, { style: 'list-style-type: ' + listStyle });
859
- }
860
-
861
- if (rangeTag?.children.length === 0) dom.utils.removeItem(rangeTag);
862
- }
863
-
864
- if (topNumber) {
865
- firstList = firstList.children[topNumber];
866
- }
867
-
868
- if (mergeBottom) {
869
- // bottomNumber = list.children.length - 1;
870
- list.innerHTML += bottomEl.innerHTML;
871
- // lastList = list.children[bottomNumber] || lastList;
872
- dom.utils.removeItem(bottomEl);
873
- }
874
- }
875
-
876
- this.editor.effectNode = null;
877
- return !isRemove || !isCollapsed ? originRange : afterRange || originRange;
878
- },
879
-
880
- /**
881
- * @this {FormatThis}
882
- * @description "selectedCells" array are detached from the list element.
883
- * - The return value is applied when the first and last lines of "selectedFormats" are "LI" respectively.
884
- * @param {Array<Node>} selectedCells Array of ["line", li] elements(LI, P...) to remove.
885
- * @param {boolean} shouldDelete If true, It does not just remove the list, it deletes the content.
886
- * @returns {{sc: Node, ec: Node}} Node information after deletion
887
- * - sc: Start container node
888
- * - ec: End container node
889
- */
890
- removeList(selectedCells, shouldDelete) {
891
- let rangeArr = {};
892
- let listFirst = false;
893
- let listLast = false;
894
- let first = null;
895
- let last = null;
896
- const passComponent = (current) => {
897
- return !this.component.is(current);
898
- };
899
-
900
- for (let i = 0, len = selectedCells.length, r, o, lastIndex, isList; i < len; i++) {
901
- lastIndex = i === len - 1;
902
- o = this.getBlock(selectedCells[i], passComponent);
903
- isList = dom.check.isList(o);
904
- if (!r && isList) {
905
- r = o;
906
- rangeArr = {
907
- r: r,
908
- f: [dom.query.getParentElement(selectedCells[i], 'LI')]
909
- };
910
- if (i === 0) listFirst = true;
911
- } else if (r && isList) {
912
- if (r !== o) {
913
- const edge = this.removeBlock(rangeArr.f[0].parentNode, { selectedFormats: rangeArr.f, newBlockElement: null, shouldDelete, skipHistory: true });
914
- o = selectedCells[i].parentNode;
915
- if (listFirst) {
916
- first = edge.sc;
917
- listFirst = false;
918
- }
919
- if (lastIndex) last = edge.ec;
920
-
921
- if (isList) {
922
- r = o;
923
- rangeArr = {
924
- r: r,
925
- f: [dom.query.getParentElement(selectedCells[i], 'LI')]
926
- };
927
- if (lastIndex) listLast = true;
928
- } else {
929
- r = null;
930
- }
931
- } else {
932
- rangeArr.f.push(dom.query.getParentElement(selectedCells[i], 'LI'));
933
- if (lastIndex) listLast = true;
934
- }
935
- }
936
-
937
- if (lastIndex && dom.check.isList(r)) {
938
- const edge = this.removeBlock(rangeArr.f[0].parentNode, { selectedFormats: rangeArr.f, newBlockElement: null, shouldDelete, skipHistory: true });
939
- if (listLast || len === 1) last = edge.ec;
940
- if (listFirst) first = edge.sc || last;
941
- }
942
- }
943
-
944
- return {
945
- sc: first,
946
- ec: last
947
- };
948
- },
949
-
950
- /**
951
- * @this {FormatThis}
952
- * @description Indent more the selected lines.
953
- * - margin size : 'status.indentSize'px
954
- */
955
- indent() {
956
- const range = this.selection.getRange();
957
- const sc = range.startContainer;
958
- const ec = range.endContainer;
959
- const so = range.startOffset;
960
- const eo = range.endOffset;
961
-
962
- const lines = this.getLines(null);
963
- const cells = SetLineMargin(lines, this.status.indentSize, this.options.get('_rtl') ? 'marginRight' : 'marginLeft');
964
-
965
- // list cells
966
- if (cells.length > 0) {
967
- this._applyNestedList(cells, false);
968
- }
969
-
970
- this.editor.effectNode = null;
971
- this.selection.setRange(sc, so, ec, eo);
972
- this.history.push(false);
973
- },
974
-
975
- /**
976
- * @this {FormatThis}
977
- * @description Indent less the selected lines.
978
- * - margin size - "status.indentSize"px
979
- */
980
- outdent() {
981
- const range = this.selection.getRange();
982
- const sc = range.startContainer;
983
- const ec = range.endContainer;
984
- const so = range.startOffset;
985
- const eo = range.endOffset;
986
-
987
- const lines = this.getLines(null);
988
- const cells = SetLineMargin(lines, this.status.indentSize * -1, this.options.get('_rtl') ? 'marginRight' : 'marginLeft');
989
-
990
- // list cells
991
- if (cells.length > 0) {
992
- this._applyNestedList(cells, true);
993
- }
994
-
995
- this.editor.effectNode = null;
996
- this.selection.setRange(sc, so, ec, eo);
997
- this.history.push(false);
998
- },
999
-
1000
- /**
1001
- * @this {FormatThis}
1002
- * @description Adds, updates, or deletes style nodes from selected text (a, span, strong, etc.).
1003
- * @param {?Node} styleNode The element to be added to the selection. If null, only existing nodes are modified or removed.
1004
- * @param {Object} [options] Options
1005
- * @param {Array<string>} [options.stylesToModify=null] Array of style or class names to check and modify.
1006
- * (e.g., ['font-size'], ['.className'], ['font-family', 'color', '.className'])
1007
- * @param {Array<string>} [options.nodesToRemove=null] Array of node names to remove.
1008
- * If empty array or null when styleNode is null, all formats are removed.
1009
- * (e.g., ['span'], ['strong', 'em'])
1010
- * @param {boolean} [options.strictRemove=false] If true, only removes nodes from nodesToRemove if all styles and classes are removed.
1011
- * @returns {HTMLElement} The element that was added to or modified in the selection.
1012
- *
1013
- * @details
1014
- * 1. If styleNode is provided, a node with the same tags and attributes is added to the selected text.
1015
- * 2. If the same tag already exists, only its attributes are updated.
1016
- * 3. If styleNode is null, existing nodes are updated or removed without adding new ones.
1017
- * 4. Styles matching those in stylesToModify are removed. (Use CSS attribute names, e.g., "background-color")
1018
- * 5. Classes matching those in stylesToModify (prefixed with ".") are removed.
1019
- * 6. stylesToModify is used to avoid duplicate property values from styleNode.
1020
- * 7. Nodes with all styles and classes removed are deleted if they match styleNode, are in nodesToRemove, or if styleNode is null.
1021
- * 8. Tags matching names in nodesToRemove are deleted regardless of their style and class.
1022
- * 9. If strictRemove is true, nodes in nodesToRemove are only removed if all their styles and classes are removed.
1023
- * 10. The function won't modify nodes if the parent has the same class and style values.
1024
- * - However, if nodesToRemove has values, it will work and separate text nodes even if there's no node to replace.
1025
- */
1026
- applyInlineElement(styleNode, { stylesToModify, nodesToRemove, strictRemove } = {}) {
1027
- if (dom.query.getParentElement(this.selection.getNode(), dom.check.isNonEditable)) return;
1028
-
1029
- this.selection._resetRangeToTextNode();
1030
- let range = this.selection.getRangeAndAddLine(this.selection.getRange(), null);
1031
- stylesToModify = stylesToModify?.length > 0 ? stylesToModify : null;
1032
- nodesToRemove = nodesToRemove?.length > 0 ? nodesToRemove : null;
1033
-
1034
- const isRemoveNode = !styleNode;
1035
- const isRemoveFormat = isRemoveNode && !nodesToRemove && !stylesToModify;
1036
- let startCon = range.startContainer;
1037
- let startOff = range.startOffset;
1038
- let endCon = range.endContainer;
1039
- let endOff = range.endOffset;
1040
-
1041
- if ((isRemoveFormat && range.collapsed && this.isLine(startCon.parentNode) && this.isLine(endCon.parentNode)) || (startCon === endCon && startCon.nodeType === 1 && dom.check.isNonEditable(startCon))) {
1042
- const format = startCon.parentNode;
1043
- if (
1044
- !dom.check.isListCell(format) ||
1045
- !converter.getValues(format.style).some((k) => {
1046
- return this._listKebab.includes(k);
1047
- })
1048
- )
1049
- return;
1050
- return;
1051
- }
1052
-
1053
- if (range.collapsed && !isRemoveFormat) {
1054
- if (startCon.nodeType === 1 && !dom.check.isBreak(startCon) && !this.component.is(startCon)) {
1055
- let afterNode = null;
1056
- const focusNode = startCon.childNodes[startOff];
1057
-
1058
- if (focusNode) {
1059
- if (!focusNode.nextSibling) {
1060
- afterNode = null;
1061
- } else {
1062
- afterNode = dom.check.isBreak(focusNode) ? focusNode : focusNode.nextSibling;
1063
- }
1064
- }
1065
-
1066
- const zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
1067
- startCon.insertBefore(zeroWidth, afterNode);
1068
- this.selection.setRange(zeroWidth, 1, zeroWidth, 1);
1069
-
1070
- range = this.selection.getRange();
1071
- startCon = range.startContainer;
1072
- startOff = range.startOffset;
1073
- endCon = range.endContainer;
1074
- endOff = range.endOffset;
1075
- }
1076
- }
1077
-
1078
- if (this.isLine(startCon)) {
1079
- startCon = startCon.childNodes[startOff] || startCon.firstChild;
1080
- startOff = 0;
1081
- }
1082
- if (this.isLine(endCon)) {
1083
- endCon = endCon.childNodes[endOff] || endCon.lastChild;
1084
- endOff = endCon.textContent.length;
1085
- }
1086
-
1087
- if (isRemoveNode) {
1088
- styleNode = dom.utils.createElement('DIV');
1089
- }
1090
-
1091
- const wRegExp = RegExp;
1092
- const newNodeName = styleNode.nodeName;
1093
-
1094
- /* checked same style property */
1095
- if (!isRemoveFormat && startCon === endCon && !nodesToRemove && styleNode) {
1096
- let sNode = startCon;
1097
- let checkCnt = 0;
1098
- const checkAttrs = [];
1099
-
1100
- const checkStyles = /** @type {HTMLElement} */ (styleNode).style;
1101
- for (let i = 0, len = checkStyles.length; i < len; i++) {
1102
- checkAttrs.push(checkStyles[i]);
1103
- }
1104
-
1105
- const checkClassName = /** @type {HTMLElement} */ (styleNode).className;
1106
- const ckeckClasses = /** @type {HTMLElement} */ (styleNode).classList;
1107
- for (let i = 0, len = ckeckClasses.length; i < len; i++) {
1108
- checkAttrs.push('.' + ckeckClasses[i]);
1109
- }
1110
-
1111
- if (checkAttrs.length > 0) {
1112
- while (!this.isLine(sNode) && !dom.check.isWysiwygFrame(sNode)) {
1113
- for (let i = 0; i < checkAttrs.length; i++) {
1114
- if (sNode.nodeType === 1) {
1115
- const s = checkAttrs[i];
1116
- const classReg = /^\./.test(s) ? new wRegExp('\\s*' + s.replace(/^\./, '') + '(\\s+|$)', 'ig') : false;
1117
- const sNodeStyle = /** @type {HTMLElement} */ (sNode).style;
1118
- const sNodeClassName = /** @type {HTMLElement} */ (sNode).className;
1119
-
1120
- const styleCheck = isRemoveNode ? !!sNodeStyle[s] : !!sNodeStyle[s] && !!checkStyles[s] && sNodeStyle[s] === checkStyles[s];
1121
- const classCheck = classReg === false ? false : isRemoveNode ? !!sNodeClassName.match(classReg) : !!sNodeClassName.match(classReg) && !!checkClassName.match(classReg);
1122
- if (styleCheck || classCheck) {
1123
- checkCnt++;
1124
- }
1125
- }
1126
- }
1127
- sNode = sNode.parentNode;
1128
- }
1129
-
1130
- if (checkCnt >= checkAttrs.length) return;
1131
- }
1132
- }
1133
-
1134
- let newNode;
1135
- /** @type {NodeStyleContainerType} */
1136
- let start = {};
1137
- /** @type {NodeStyleContainerType} */
1138
- let end = {};
1139
-
1140
- /** @type {string|RegExp} */
1141
- let styleRegExp = '';
1142
- /** @type {string|RegExp} */
1143
- let classRegExp = '';
1144
- /** @type {string|RegExp} */
1145
- let removeNodeRegExp;
1146
-
1147
- if (stylesToModify) {
1148
- for (let i = 0, len = stylesToModify.length, s; i < len; i++) {
1149
- s = stylesToModify[i];
1150
- if (/^\./.test(s)) {
1151
- classRegExp += (classRegExp ? '|' : '\\s*(?:') + s.replace(/^\./, '');
1152
- } else {
1153
- styleRegExp += (styleRegExp ? '|' : '(?:;|^|\\s)(?:') + s;
1154
- }
1155
- }
1156
-
1157
- if (styleRegExp) {
1158
- styleRegExp += ')\\s*:[^;]*\\s*(?:;|$)';
1159
- styleRegExp = new wRegExp(styleRegExp, 'ig');
1160
- }
1161
-
1162
- if (classRegExp) {
1163
- classRegExp += ')(?=\\s+|$)';
1164
- classRegExp = new wRegExp(classRegExp, 'ig');
1165
- }
1166
- }
1167
-
1168
- if (nodesToRemove) {
1169
- removeNodeRegExp = '^(?:' + nodesToRemove[0];
1170
- for (let i = 1; i < nodesToRemove.length; i++) {
1171
- removeNodeRegExp += '|' + nodesToRemove[i];
1172
- }
1173
- removeNodeRegExp += ')$';
1174
- removeNodeRegExp = new wRegExp(removeNodeRegExp, 'i');
1175
- }
1176
-
1177
- /** validation check function*/
1178
- const _removeCheck = {
1179
- v: false
1180
- };
1181
- const validation = function (checkNode) {
1182
- const vNode = checkNode.cloneNode(false);
1183
-
1184
- // all path
1185
- if (vNode.nodeType === 3 || dom.check.isBreak(vNode)) return vNode;
1186
- // all remove
1187
- if (isRemoveFormat) return null;
1188
-
1189
- // remove node check
1190
- const tagRemove = (!removeNodeRegExp && isRemoveNode) || /** @type {RegExp} */ (removeNodeRegExp)?.test(vNode.nodeName);
1191
-
1192
- // tag remove
1193
- if (tagRemove && !strictRemove) {
1194
- _removeCheck.v = true;
1195
- return null;
1196
- }
1197
-
1198
- // style regexp
1199
- const originStyle = vNode.style.cssText;
1200
- let style = '';
1201
- if (styleRegExp && originStyle.length > 0) {
1202
- style = originStyle.replace(styleRegExp, '').trim();
1203
- if (style !== originStyle) _removeCheck.v = true;
1204
- }
1205
-
1206
- // class check
1207
- const originClasses = vNode.className;
1208
- let classes = '';
1209
- if (classRegExp && originClasses.length > 0) {
1210
- classes = originClasses.replace(classRegExp, '').trim();
1211
- if (classes !== originClasses) _removeCheck.v = true;
1212
- }
1213
-
1214
- // remove only
1215
- if (isRemoveNode) {
1216
- if ((classRegExp || !originClasses) && (styleRegExp || !originStyle) && !style && !classes && tagRemove) {
1217
- _removeCheck.v = true;
1218
- return null;
1219
- }
1220
- }
1221
-
1222
- // change
1223
- if (style || classes || vNode.nodeName !== newNodeName || Boolean(styleRegExp) !== Boolean(originStyle) || Boolean(classRegExp) !== Boolean(originClasses)) {
1224
- if (styleRegExp && originStyle.length > 0) vNode.style.cssText = style;
1225
- if (!vNode.style.cssText) {
1226
- vNode.removeAttribute('style');
1227
- }
1228
-
1229
- if (classRegExp && originClasses.length > 0) vNode.className = classes.trim();
1230
- if (!vNode.className.trim()) {
1231
- vNode.removeAttribute('class');
1232
- }
1233
-
1234
- if (!vNode.style.cssText && !vNode.className && (vNode.nodeName === newNodeName || tagRemove)) {
1235
- _removeCheck.v = true;
1236
- return null;
1237
- }
1238
-
1239
- return vNode;
1240
- }
1241
-
1242
- _removeCheck.v = true;
1243
- return null;
1244
- };
1245
-
1246
- // get line nodes
1247
- const lineNodes = this.getLines(null);
1248
- range = this.selection.getRange();
1249
- startCon = range.startContainer;
1250
- startOff = range.startOffset;
1251
- endCon = range.endContainer;
1252
- endOff = range.endOffset;
1253
-
1254
- if (!this.getLine(startCon, null)) {
1255
- startCon = dom.query.getEdgeChild(
1256
- lineNodes[0],
1257
- function (current) {
1258
- return current.nodeType === 3;
1259
- },
1260
- false
1261
- );
1262
- startOff = 0;
1263
- }
1264
-
1265
- if (!this.getLine(endCon, null)) {
1266
- endCon = dom.query.getEdgeChild(
1267
- lineNodes[lineNodes.length - 1],
1268
- function (current) {
1269
- return current.nodeType === 3;
1270
- },
1271
- false
1272
- );
1273
- endOff = endCon.textContent.length;
1274
- }
1275
-
1276
- const oneLine = this.getLine(startCon, null) === this.getLine(endCon, null);
1277
- const endLength = lineNodes.length - (oneLine ? 0 : 1);
1278
-
1279
- // node Changes
1280
- newNode = styleNode.cloneNode(false);
1281
-
1282
- const isRemoveAnchor =
1283
- isRemoveFormat ||
1284
- (isRemoveNode &&
1285
- (function (inst, arr) {
1286
- for (let n = 0, len = arr.length; n < len; n++) {
1287
- if (inst._isNonSplitNode(arr[n])) return true;
1288
- }
1289
- return false;
1290
- })(this, nodesToRemove));
1291
-
1292
- const isSizeNode = isRemoveNode || this._sn_isSizeNode(newNode);
1293
- const _getMaintainedNode = this._sn_getMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
1294
- const _isMaintainedNode = this._sn_isMaintainedNode.bind(this, isRemoveAnchor, isSizeNode);
1295
-
1296
- // one line
1297
- if (oneLine) {
1298
- if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) range = this.selection.setRange(startCon, startOff, endCon, endOff);
1299
-
1300
- const newRange = this._setNode_oneLine(lineNodes[0], newNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, range.collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode);
1301
- start.container = newRange.startContainer;
1302
- start.offset = newRange.startOffset;
1303
- end.container = newRange.endContainer;
1304
- end.offset = newRange.endOffset;
1305
-
1306
- if (start.container === end.container && dom.check.isZeroWidth(start.container)) {
1307
- start.offset = end.offset = 1;
1308
- }
1309
- this._sn_setCommonListStyle(newRange.ancestor, null);
1310
- } else {
1311
- // multi line
1312
- let appliedCommonList = false;
1313
- if (endLength > 0 && this._sn_resetCommonListCell(lineNodes[endLength], stylesToModify)) appliedCommonList = true;
1314
- if (this._sn_resetCommonListCell(lineNodes[0], stylesToModify)) appliedCommonList = true;
1315
- if (appliedCommonList) this.selection.setRange(startCon, startOff, endCon, endOff);
1316
-
1317
- // end
1318
- if (endLength > 0) {
1319
- newNode = styleNode.cloneNode(false);
1320
- end = this._setNode_endLine(lineNodes[endLength], newNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode);
1321
- }
1322
-
1323
- // mid
1324
- for (let i = endLength - 1, newRange; i > 0; i--) {
1325
- this._sn_resetCommonListCell(lineNodes[i], stylesToModify);
1326
- newNode = styleNode.cloneNode(false);
1327
- newRange = this._setNode_middleLine(lineNodes[i], newNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, end.container);
1328
- if (newRange.endContainer && newRange.ancestor.contains(newRange.endContainer)) {
1329
- end.ancestor = null;
1330
- end.container = newRange.endContainer;
1331
- }
1332
- this._sn_setCommonListStyle(newRange.ancestor, null);
1333
- }
1334
-
1335
- // start
1336
- newNode = styleNode.cloneNode(false);
1337
- start = this._setNode_startLine(lineNodes[0], newNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, end.container);
1338
-
1339
- if (start.endContainer) {
1340
- end.ancestor = null;
1341
- end.container = start.endContainer;
1342
- }
1343
-
1344
- if (endLength <= 0) {
1345
- end = start;
1346
- } else if (!end.container) {
1347
- end.ancestor = null;
1348
- end.container = start.container;
1349
- end.offset = start.container.textContent.length;
1350
- }
1351
-
1352
- this._sn_setCommonListStyle(start.ancestor, null);
1353
- this._sn_setCommonListStyle(end.ancestor || this.getLine(end.container), null);
1354
- }
1355
-
1356
- // set range
1357
- this.ui._offCurrentController();
1358
- this.selection.setRange(start.container, start.offset, end.container, end.offset);
1359
- this.history.push(false);
1360
-
1361
- return /** @type {HTMLElement} */ (newNode);
1362
- },
1363
-
1364
- /**
1365
- * @this {FormatThis}
1366
- * @description Remove format of the currently selected text.
1367
- */
1368
- removeInlineElement() {
1369
- this.applyInlineElement(null, { stylesToModify: null, nodesToRemove: null, strictRemove: null });
1370
- },
1371
-
1372
- /**
1373
- * @this {FormatThis}
1374
- * @description Check if the container and offset values are the edges of the "line"
1375
- * @param {Node} node The node of the selection object. (range.startContainer..)
1376
- * @param {number} offset The offset of the selection object. (selection.getRange().startOffset...)
1377
- * @param {"front"|"end"} dir Select check point - "front": Front edge, "end": End edge, undefined: Both edge.
1378
- * @returns {node is HTMLElement}
1379
- */
1380
- isEdgeLine(node, offset, dir) {
1381
- if (!dom.check.isEdgePoint(node, offset, dir)) return false;
1382
-
1383
- let result = false;
1384
- const siblingType = dir === 'front' ? 'previousSibling' : 'nextSibling';
1385
- while (node && !this.isLine(node) && !dom.check.isWysiwygFrame(node)) {
1386
- if (!node[siblingType] || (dom.check.isBreak(node[siblingType]) && !node[siblingType][siblingType])) {
1387
- result = true;
1388
- node = node.parentNode;
1389
- } else {
1390
- return false;
1391
- }
1392
- }
1393
-
1394
- return result;
1395
- },
1396
-
1397
- /**
1398
- * @this {FormatThis}
1399
- * @description It is judged whether it is a node related to the text style.
1400
- * @param {Node|string} element The node to check
1401
- * @returns {element is HTMLElement}
1402
- */
1403
- isTextStyleNode(element) {
1404
- return typeof element === 'string' ? this._textStyleTagsCheck.test(element) : element && element.nodeType === 1 && this._textStyleTagsCheck.test(element.nodeName);
1405
- },
1406
-
1407
- /**
1408
- * @this {FormatThis}
1409
- * @description It is judged whether it is the "line" element.
1410
- * - (P, DIV, H[1-6], PRE, LI | class="__se__format__line_xxx")
1411
- * - "line" element also contain "brLine" element
1412
- * @param {Node|string} element The node to check
1413
- * @returns {element is HTMLElement}
1414
- */
1415
- isLine(element) {
1416
- if (this.isBlock(element)) return false;
1417
- return typeof element === 'string'
1418
- ? this._formatLineCheck.test(element)
1419
- : element && element.nodeType === 1 && (this._formatLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__line_.+|__se__format__br_line_.+')) && !this._nonFormat(element);
1420
- },
1421
-
1422
- /**
1423
- * @this {FormatThis}
1424
- * @description It is judged whether it is the only "line" element, not "brLine".
1425
- * @param {Node|string} element The node to check
1426
- * @returns {element is HTMLElement}
1427
- */
1428
- isLineOnly(element) {
1429
- return this.isLine(element) && (this._brLineBreak || !this.isBrLine(element));
1430
- },
1431
-
1432
- /**
1433
- * @this {FormatThis}
1434
- * @description It is judged whether it is the "brLine" element.
1435
- * - (PRE | class="__se__format__br_line_xxx")
1436
- * - "brLine" elements is included in the "line" element.
1437
- * - "brLine" elements's line break is "BR" tag.
1438
- * ※ Entering the Enter key in the space on the last line ends "brLine" and appends "line".
1439
- * @param {Node|string} element The node to check
1440
- * @returns {element is HTMLElement}
1441
- */
1442
- isBrLine(element) {
1443
- return (
1444
- (this._brLineBreak && this.isLine(element)) ||
1445
- (typeof element === 'string'
1446
- ? this._formatBrLineCheck.test(element)
1447
- : element && element.nodeType === 1 && (this._formatBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line_.+')) && !this._nonFormat(element))
1448
- );
1449
- },
1450
-
1451
- /**
1452
- * @this {FormatThis}
1453
- * @description It is judged whether it is the "block" element.
1454
- * - (BLOCKQUOTE, OL, UL, FIGCAPTION, TABLE, THEAD, TBODY, TR, TH, TD | class="__se__format__block_xxx")
1455
- * - "block" is wrap the "line" and "component"
1456
- * @param {Node|string} element The node to check
1457
- * @returns {element is HTMLElement}
1458
- */
1459
- isBlock(element) {
1460
- return typeof element === 'string'
1461
- ? this._formatBlockCheck.test(element)
1462
- : element && element.nodeType === 1 && (this._formatBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_.+')) && !this._nonFormat(element);
1463
- },
1464
-
1465
- /**
1466
- * @this {FormatThis}
1467
- * @description It is judged whether it is the "closureBlock" element.
1468
- * - (TH, TD | class="__se__format__block_closure_xxx")
1469
- * - "closureBlock" elements is included in the "block".
1470
- * - "closureBlock" element is wrap the "line" and "component"
1471
- * - ※ You cannot exit this format with the Enter key or Backspace key.
1472
- * - ※ Use it only in special cases. ([ex] format of table cells)
1473
- * @param {Node|string} element The node to check
1474
- * @returns {element is HTMLElement}
1475
- */
1476
- isClosureBlock(element) {
1477
- return typeof element === 'string'
1478
- ? this._formatClosureBlockCheck.test(element)
1479
- : element && element.nodeType === 1 && (this._formatClosureBlockCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__block_closure_.+')) && !this._nonFormat(element);
1480
- },
1481
-
1482
- /**
1483
- * @this {FormatThis}
1484
- * @description It is judged whether it is the "closureBrLine" element.
1485
- * - (class="__se__format__br_line__closure_xxx")
1486
- * - "closureBrLine" elements is included in the "brLine".
1487
- * - "closureBrLine" elements's line break is "BR" tag.
1488
- * - ※ You cannot exit this format with the Enter key or Backspace key.
1489
- * - ※ Use it only in special cases. ([ex] format of table cells)
1490
- * @param {Node|string} element The node to check
1491
- * @returns {element is HTMLElement}
1492
- */
1493
- isClosureBrLine(element) {
1494
- return typeof element === 'string'
1495
- ? this._formatClosureBrLineCheck.test(element)
1496
- : element && element.nodeType === 1 && (this._formatClosureBrLineCheck.test(element.nodeName) || dom.utils.hasClass(element, '__se__format__br_line__closure_.+')) && !this._nonFormat(element);
1497
- },
1498
-
1499
- /**
1500
- * @this {FormatThis}
1501
- * @description Returns a "line" array from selected range.
1502
- * @param {?(current: Node) => boolean=} validation The validation function. (Replaces the default validation format.isLine(current))
1503
- * @returns {Array<HTMLElement>}
1504
- */
1505
- getLines(validation) {
1506
- if (!this.selection._resetRangeToTextNode()) return [];
1507
- let range = this.selection.getRange();
1508
-
1509
- if (dom.check.isWysiwygFrame(range.startContainer)) {
1510
- const children = this.editor.frameContext.get('wysiwyg').children;
1511
- if (children.length === 0) return [];
1512
-
1513
- this.selection.setRange(children[0], 0, children[children.length - 1], children[children.length - 1].textContent.trim().length);
1514
- range = this.selection.getRange();
1515
- }
1516
-
1517
- const startCon = range.startContainer;
1518
- const endCon = range.endContainer;
1519
- const commonCon = range.commonAncestorContainer;
1520
-
1521
- // get line nodes
1522
- const lineNodes = dom.query.getListChildren(commonCon, (current) => {
1523
- return validation ? validation(current) : this.isLine(current);
1524
- });
1525
-
1526
- if (!dom.check.isWysiwygFrame(commonCon) && !this.isBlock(commonCon)) lineNodes.unshift(this.getLine(commonCon, null));
1527
- if (startCon === endCon || lineNodes.length === 1) return lineNodes;
1528
-
1529
- const startLine = this.getLine(startCon, null);
1530
- const endLine = this.getLine(endCon, null);
1531
- let startIdx = null;
1532
- let endIdx = null;
1533
-
1534
- const onlyTable = function (current) {
1535
- return dom.check.isTableElements(current) ? /^TABLE$/i.test(current.nodeName) : true;
1536
- };
1537
-
1538
- let startRangeEl = this.getBlock(startLine, onlyTable);
1539
- let endRangeEl = this.getBlock(endLine, onlyTable);
1540
- if (dom.check.isTableElements(startRangeEl) && dom.check.isListCell(startRangeEl.parentNode)) startRangeEl = startRangeEl.parentNode;
1541
- if (dom.check.isTableElements(endRangeEl) && dom.check.isListCell(endRangeEl.parentNode)) endRangeEl = endRangeEl.parentNode;
1542
-
1543
- const sameRange = startRangeEl === endRangeEl;
1544
- for (let i = 0, len = lineNodes.length, line; i < len; i++) {
1545
- line = lineNodes[i];
1546
-
1547
- if (startLine === line || (!sameRange && line === startRangeEl)) {
1548
- startIdx = i;
1549
- continue;
1550
- }
1551
-
1552
- if (endLine === line || (!sameRange && line === endRangeEl)) {
1553
- endIdx = i;
1554
- break;
1555
- }
1556
- }
1557
-
1558
- if (startIdx === null) startIdx = 0;
1559
- if (endIdx === null) endIdx = lineNodes.length - 1;
1560
-
1561
- return lineNodes.slice(startIdx, endIdx + 1);
1562
- },
1563
-
1564
- /**
1565
- * @this {FormatThis}
1566
- * @description Get lines and components from the selected range. (P, DIV, H[1-6], OL, UL, TABLE..)
1567
- * - If some of the component are included in the selection, get the entire that component.
1568
- * @param {boolean} removeDuplicate If true, if there is a parent and child tag among the selected elements, the child tag is excluded.
1569
- * @returns {Array<HTMLElement>}
1570
- */
1571
- getLinesAndComponents(removeDuplicate) {
1572
- const commonCon = this.selection.getRange().commonAncestorContainer;
1573
- const myComponent = dom.query.getParentElement(commonCon, this.component.is.bind(this.component));
1574
- const selectedLines = dom.check.isTableElements(commonCon)
1575
- ? this.getLines(null)
1576
- : this.getLines((current) => {
1577
- const component = dom.query.getParentElement(current, this.component.is.bind(this.component));
1578
- return (this.isLine(current) && (!component || component === myComponent)) || (this.component.is(current) && !this.getLine(current));
1579
- });
1580
-
1581
- if (removeDuplicate) {
1582
- for (let i = 0, len = selectedLines.length; i < len; i++) {
1583
- for (let j = i - 1; j >= 0; j--) {
1584
- if (selectedLines[j].contains(selectedLines[i])) {
1585
- selectedLines.splice(i, 1);
1586
- i--;
1587
- len--;
1588
- break;
1589
- }
1590
- }
1591
- }
1592
- }
1593
-
1594
- return selectedLines;
1595
- },
1596
-
1597
- /**
1598
- * @private
1599
- * @this {FormatThis}
1600
- * @description A function that distinguishes areas where "selection" should not be placed
1601
- * @param {Node} element Element
1602
- * @returns {boolean}
1603
- */
1604
- _isExcludeSelectionElement(element) {
1605
- return !/FIGCAPTION/i.test(element.nodeName) && (this.component.is(element) || /FIGURE/i.test(element.nodeName));
1606
- },
1607
-
1608
- /**
1609
- * @private
1610
- * @this {FormatThis}
1611
- * @description A function that distinguishes non-formatting HTML elements or tags from formatting ones.
1612
- * @param {Node} element Element
1613
- * @returns {boolean}
1614
- */
1615
- _nonFormat(element) {
1616
- return dom.check.isExcludeFormat(element) || this.component.is(element) || dom.check.isWysiwygFrame(element);
1617
- },
1618
-
1619
- /**
1620
- * @private
1621
- * @this {FormatThis}
1622
- * @description Nodes that must remain undetached when changing text nodes (A, Label, Code, Span:font-size)
1623
- * @param {Node|string} element Element to check
1624
- * @returns {boolean}
1625
- */
1626
- _isNonSplitNode(element) {
1627
- if (!element) return false;
1628
- const checkRegExp = /^(a|label|code|summary)$/i;
1629
- if (typeof element === 'string') return checkRegExp.test(element);
1630
- return element.nodeType === 1 && checkRegExp.test(element.nodeName);
1631
- },
1632
-
1633
- /**
1634
- * @private
1635
- * @this {FormatThis}
1636
- * @description Nodes without text
1637
- * @param {Node|string} element Element to check
1638
- * @returns {boolean}
1639
- */
1640
- _notTextNode(element) {
1641
- if (!element) return false;
1642
- const checkRegExp = /^(br|input|select|canvas|img|iframe|audio|video)$/i;
1643
- if (typeof element === 'string') return checkRegExp.test(element);
1644
- return element.nodeType === 1 && (this.component.is(element) || checkRegExp.test(element.nodeName));
1645
- },
1646
-
1647
- /**
1648
- * @private
1649
- * @this {FormatThis}
1650
- * @description Nodes that need to be added without modification when changing text nodes
1651
- * @param {Node} element Element to check
1652
- * @returns {boolean}
1653
- */
1654
- _isIgnoreNodeChange(element) {
1655
- return element && element.nodeType === 1 && (dom.check.isNonEditable(element) || !this.isTextStyleNode(element) || this.component.is(element));
1656
- },
1657
-
1658
- /**
1659
- * @private
1660
- * @this {FormatThis}
1661
- * @description Get current selected lines and selected node info.
1662
- * @returns {{lines: Array<HTMLElement>, firstNode: Node, lastNode: Node, firstPath: Array<number>, lastPath: Array<number>, startOffset: number, endOffset: number}}
1663
- */
1664
- _lineWork() {
1665
- let range = this.selection.getRange();
1666
- let selectedFormsts = this.getLinesAndComponents(false);
1667
-
1668
- if (selectedFormsts.length === 0) {
1669
- range = this.selection.getRangeAndAddLine(range, null);
1670
- selectedFormsts = this.getLinesAndComponents(false);
1671
- if (selectedFormsts.length === 0) return;
1672
- }
1673
-
1674
- const startOffset = range.startOffset;
1675
- const endOffset = range.endOffset;
1676
-
1677
- let first = /** @type {Node} */ (selectedFormsts[0]);
1678
- let last = /** @type {Node} */ (selectedFormsts[selectedFormsts.length - 1]);
1679
- const firstPath = dom.query.getNodePath(range.startContainer, first, null);
1680
- const lastPath = dom.query.getNodePath(range.endContainer, last, null);
1681
-
1682
- // remove selected list
1683
- const rlist = this.removeList(selectedFormsts, false);
1684
- if (rlist.sc) first = rlist.sc;
1685
- if (rlist.ec) last = rlist.ec;
1686
-
1687
- // change format tag
1688
- this.selection.setRange(dom.query.getNodeFromPath(firstPath, first), startOffset, dom.query.getNodeFromPath(lastPath, last), endOffset);
1689
-
1690
- return {
1691
- lines: this.getLinesAndComponents(false),
1692
- firstNode: first,
1693
- lastNode: last,
1694
- firstPath: firstPath,
1695
- lastPath: lastPath,
1696
- startOffset: startOffset,
1697
- endOffset: endOffset
1698
- };
1699
- },
1700
-
1701
- /**
1702
- * @private
1703
- * @this {FormatThis}
1704
- * @description Attaches a nested list structure by merging adjacent lists if applicable.
1705
- * - Ensures that the nested list is placed correctly in the document structure.
1706
- * @param {Element} originList The original list element where the nested list is inserted.
1707
- * @param {Element} innerList The nested list element.
1708
- * @param {Element} prev The previous sibling element.
1709
- * @param {Element} next The next sibling element.
1710
- * @param {{s: Array<number> | null, e: Array<number> | null, sl: Node | null, el: Node | null}} nodePath Object storing the start and end node paths.
1711
- * - s : Start node path.
1712
- * - e : End node path.
1713
- * - sl : Start node's parent element.
1714
- * - el : End node's parent element.
1715
- * @returns {Node} The attached inner list.
1716
- */
1717
- _attachNested(originList, innerList, prev, next, nodePath) {
1718
- let insertPrev = false;
1719
-
1720
- if (innerList.tagName === prev?.tagName) {
1721
- const children = innerList.children;
1722
- while (children[0]) {
1723
- prev.appendChild(children[0]);
1724
- }
1725
-
1726
- innerList = prev;
1727
- insertPrev = true;
1728
- }
1729
-
1730
- if (innerList.tagName === next?.tagName) {
1731
- const children = next.children;
1732
- while (children[0]) {
1733
- innerList.appendChild(children[0]);
1734
- }
1735
-
1736
- const temp = next.nextElementSibling;
1737
- next.parentNode.removeChild(next);
1738
- next = temp;
1739
- }
1740
-
1741
- if (!insertPrev) {
1742
- if (dom.check.isListCell(prev)) {
1743
- originList = prev;
1744
- next = null;
1745
- }
1746
-
1747
- originList.insertBefore(innerList, next);
1748
-
1749
- if (!nodePath.s) {
1750
- nodePath.s = dom.query.getNodePath(innerList.firstElementChild.firstChild, originList, null);
1751
- nodePath.sl = originList;
1752
- }
1753
-
1754
- const slPath = originList.contains(nodePath.sl) ? dom.query.getNodePath(nodePath.sl, originList) : null;
1755
- nodePath.e = dom.query.getNodePath(innerList.lastElementChild.firstChild, originList, null);
1756
- nodePath.el = originList;
1757
-
1758
- this.nodeTransform.mergeSameTags(originList, [nodePath.s, nodePath.e, slPath], false);
1759
- this.nodeTransform.mergeNestedTags(originList);
1760
- if (slPath) nodePath.sl = dom.query.getNodeFromPath(slPath, originList);
1761
- }
1762
-
1763
- return innerList;
1764
- },
1765
-
1766
- /**
1767
- * @private
1768
- * @this {FormatThis}
1769
- * @description Detaches a nested list structure by extracting list items from their parent list.
1770
- * - Ensures proper restructuring of the list elements.
1771
- * @param {Array<HTMLElement>} cells The list items to be detached.
1772
- * @returns {{cc: Node, sc: Node, ec: Node}} An object containing reference nodes for repositioning.
1773
- * - cc : The parent node of the first list item.
1774
- * - sc : The first list item.
1775
- * - ec : The last list item.
1776
- */
1777
- _detachNested(cells) {
1778
- const first = cells[0];
1779
- const last = cells[cells.length - 1];
1780
- const next = last.nextElementSibling;
1781
- const originList = first.parentElement;
1782
- const sibling = originList.parentElement.nextElementSibling;
1783
- const parentNode = originList.parentElement.parentElement;
1784
-
1785
- for (let c = 0, cLen = cells.length; c < cLen; c++) {
1786
- parentNode.insertBefore(cells[c], sibling);
1787
- }
1788
-
1789
- if (next && originList.children.length > 0) {
1790
- const newList = originList.cloneNode(false);
1791
- const children = originList.childNodes;
1792
- const index = dom.query.getPositionIndex(next);
1793
- while (children[index]) {
1794
- newList.appendChild(children[index]);
1795
- }
1796
- last.appendChild(newList);
1797
- }
1798
-
1799
- if (originList.children.length === 0) dom.utils.removeItem(originList);
1800
- this.nodeTransform.mergeSameTags(parentNode);
1801
-
1802
- const edge = dom.query.getEdgeChildNodes(first, last);
1803
-
1804
- return {
1805
- cc: first.parentNode,
1806
- sc: edge.sc,
1807
- ec: edge.ec
1808
- };
1809
- },
1810
-
1811
- /**
1812
- * @private
1813
- * @this {FormatThis}
1814
- * @description Nest list cells or cancel nested cells.
1815
- * @param selectedCells List cells.
1816
- * @param nested Nested or cancel nested.
1817
- */
1818
- _applyNestedList(selectedCells, nested) {
1819
- selectedCells = !selectedCells
1820
- ? this.getLines().filter(function (el) {
1821
- return dom.check.isListCell(el);
1822
- })
1823
- : selectedCells;
1824
- const cellsLen = selectedCells.length;
1825
- if (cellsLen === 0 || (!nested && !dom.check.isListCell(selectedCells[0].previousElementSibling) && !dom.check.isListCell(selectedCells[cellsLen - 1].nextElementSibling))) {
1826
- return {
1827
- sc: selectedCells[0],
1828
- so: 0,
1829
- ec: selectedCells[cellsLen - 1],
1830
- eo: 1
1831
- };
1832
- }
1833
-
1834
- let originList = selectedCells[0].parentNode;
1835
- let lastCell = selectedCells[cellsLen - 1];
1836
- let range = null;
1837
-
1838
- if (nested) {
1839
- if (originList !== lastCell.parentNode && dom.check.isList(lastCell.parentNode.parentNode) && lastCell.nextElementSibling) {
1840
- lastCell = lastCell.nextElementSibling;
1841
- while (lastCell) {
1842
- selectedCells.push(lastCell);
1843
- lastCell = lastCell.nextElementSibling;
1844
- }
1845
- }
1846
- range = this.applyList(originList.nodeName + ':' + originList.style.listStyleType, selectedCells, true);
1847
- } else {
1848
- let innerList = dom.utils.createElement(originList.nodeName);
1849
- let prev = selectedCells[0].previousElementSibling;
1850
- let next = lastCell.nextElementSibling;
1851
- const nodePath = {
1852
- s: null,
1853
- e: null,
1854
- sl: originList,
1855
- el: originList
1856
- };
1857
-
1858
- for (let i = 0, len = cellsLen, c; i < len; i++) {
1859
- c = selectedCells[i];
1860
- if (c.parentNode !== originList) {
1861
- this._attachNested(originList, innerList, prev, next, nodePath);
1862
- originList = c.parentNode;
1863
- innerList = dom.utils.createElement(originList.nodeName);
1864
- }
1865
-
1866
- prev = c.previousElementSibling;
1867
- next = c.nextElementSibling;
1868
- innerList.appendChild(c);
1869
- }
1870
-
1871
- this._attachNested(originList, innerList, prev, next, nodePath);
1872
-
1873
- const sc = dom.query.getNodeFromPath(nodePath.s, nodePath.sl);
1874
- const ec = dom.query.getNodeFromPath(nodePath.e, nodePath.el);
1875
- range = {
1876
- sc: sc,
1877
- so: 0,
1878
- ec: ec,
1879
- eo: ec.textContent.length
1880
- };
1881
- }
1882
-
1883
- return range;
1884
- },
1885
-
1886
- /**
1887
- * @private
1888
- * @this {FormatThis}
1889
- * @description Detach Nested all nested lists under the "baseNode".
1890
- * - Returns a list with nested removed.
1891
- * @param {HTMLElement} baseNode Element on which to base.
1892
- * @param {boolean} all If true, it also detach all nested lists of a returned list.
1893
- * @returns {Node} Result element
1894
- */
1895
- _removeNestedList(baseNode, all) {
1896
- const rNode = DeleteNestedList(baseNode);
1897
- let rangeElement, cNodes;
1898
-
1899
- if (rNode) {
1900
- rangeElement = rNode.cloneNode(false);
1901
- cNodes = rNode.childNodes;
1902
- const index = dom.query.getPositionIndex(baseNode);
1903
- while (cNodes[index]) {
1904
- rangeElement.appendChild(cNodes[index]);
1905
- }
1906
- } else {
1907
- rangeElement = baseNode;
1908
- }
1909
-
1910
- let rChildren;
1911
- if (!all) {
1912
- const depth = dom.query.getNodeDepth(baseNode) + 2;
1913
- rChildren = dom.query.getListChildren(baseNode, (current) => {
1914
- return dom.check.isListCell(current) && !current.previousElementSibling && dom.query.getNodeDepth(current) === depth;
1915
- });
1916
- } else {
1917
- rChildren = dom.query.getListChildren(rangeElement, (current) => {
1918
- return dom.check.isListCell(current) && !current.previousElementSibling;
1919
- });
1920
- }
1921
-
1922
- for (let i = 0, len = rChildren.length; i < len; i++) {
1923
- DeleteNestedList(rChildren[i]);
1924
- }
1925
-
1926
- if (rNode) {
1927
- rNode.parentNode.insertBefore(rangeElement, rNode.nextSibling);
1928
- if (cNodes?.length === 0) dom.utils.removeItem(rNode);
1929
- }
1930
-
1931
- return rangeElement === baseNode ? rangeElement.parentNode : rangeElement;
1932
- },
1933
-
1934
- /**
1935
- * @private
1936
- * @this {FormatThis}
1937
- * @description wraps text nodes of line selected text.
1938
- * @param {Node} element The node of the line that contains the selected text node.
1939
- * @param {Node} newInnerNode The dom that will wrap the selected text area
1940
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
1941
- * @param {Node} startCon The startContainer property of the selection object.
1942
- * @param {number} startOff The startOffset property of the selection object.
1943
- * @param {Node} endCon The endContainer property of the selection object.
1944
- * @param {number} endOff The endOffset property of the selection object.
1945
- * @param {boolean} isRemoveFormat Is the remove all formats command?
1946
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
1947
- * @param {boolean} collapsed range.collapsed
1948
- * @returns {{ancestor: *, startContainer: *, startOffset: *, endContainer: *, endOffset: *}}
1949
- */
1950
- _setNode_oneLine(element, newInnerNode, validation, startCon, startOff, endCon, endOff, isRemoveFormat, isRemoveNode, collapsed, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
1951
- // not add tag
1952
- let parentCon = startCon.parentNode;
1953
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
1954
- if (parentCon.nodeName === newInnerNode.nodeName) break;
1955
- parentCon = parentCon.parentNode;
1956
- }
1957
-
1958
- if (!isRemoveNode && parentCon === endCon.parentNode && parentCon.nodeName === newInnerNode.nodeName) {
1959
- if (dom.check.isZeroWidth(startCon.textContent.slice(0, startOff)) && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
1960
- const children = parentCon.childNodes;
1961
- let sameTag = false;
1962
-
1963
- for (let i = 0, len = children.length, c, s, e, z; i < len; i++) {
1964
- c = children[i];
1965
- z = !dom.check.isZeroWidth(c);
1966
- if (c === startCon) {
1967
- s = true;
1968
- continue;
1969
- }
1970
- if (c === endCon) {
1971
- e = true;
1972
- continue;
1973
- }
1974
- if ((!s && z) || (s && e && z)) {
1975
- sameTag = false;
1976
- break;
1977
- }
1978
- }
1979
-
1980
- if (sameTag) {
1981
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
1982
-
1983
- return {
1984
- ancestor: element,
1985
- startContainer: startCon,
1986
- startOffset: startOff,
1987
- endContainer: endCon,
1988
- endOffset: endOff
1989
- };
1990
- }
1991
- }
1992
- }
1993
-
1994
- // add tag
1995
- _removeCheck.v = false;
1996
- // eslint-disable-next-line @typescript-eslint/no-this-alias
1997
- const inst = this;
1998
- const el = element;
1999
- const nNodeArray = [newInnerNode];
2000
- const pNode = element.cloneNode(false);
2001
- const isSameNode = startCon === endCon;
2002
- let startContainer = startCon;
2003
- let startOffset = startOff;
2004
- let endContainer = endCon;
2005
- let endOffset = endOff;
2006
- let startPass = false;
2007
- let endPass = false;
2008
- let pCurrent, newNode, appendNode, cssText, anchorNode;
2009
-
2010
- const wRegExp = RegExp;
2011
- function checkCss(vNode) {
2012
- const regExp = new wRegExp('(?:;|^|\\s)(?:' + cssText + 'null)\\s*:[^;]*\\s*(?:;|$)', 'ig');
2013
- let style = false;
2014
-
2015
- if (regExp && vNode.style.cssText.length > 0) {
2016
- style = regExp.test(vNode.style.cssText);
2017
- }
2018
-
2019
- return !style;
2020
- }
2021
-
2022
- (function recursionFunc(current, ancestor) {
2023
- const childNodes = current.childNodes;
2024
-
2025
- for (let i = 0, len = childNodes.length, vNode; i < len; i++) {
2026
- const child = childNodes[i];
2027
- if (!child) continue;
2028
- let coverNode = ancestor;
2029
- let cloneNode;
2030
-
2031
- // startContainer
2032
- if (!startPass && child === startContainer) {
2033
- let line = pNode;
2034
- anchorNode = _getMaintainedNode(child);
2035
-
2036
- let _prevText = '';
2037
- let _nextText = '';
2038
- if (startContainer.nodeType === 3) {
2039
- const sText = /** @type {Text} */ (startContainer);
2040
- _prevText = sText.substringData(0, startOffset);
2041
- _nextText = sText.substringData(startOffset, isSameNode ? (endOffset >= startOffset ? endOffset - startOffset : sText.data.length - startOffset) : sText.data.length - startOffset);
2042
- }
2043
-
2044
- const prevNode = dom.utils.createTextNode(_prevText);
2045
- const textNode = dom.utils.createTextNode(_nextText);
2046
-
2047
- if (anchorNode) {
2048
- const a = _getMaintainedNode(ancestor);
2049
- if (a.parentNode !== line) {
2050
- let m = a;
2051
- let p = null;
2052
- while (m.parentNode !== line) {
2053
- ancestor = p = m.parentNode.cloneNode(false);
2054
- while (m.childNodes[0]) {
2055
- p.appendChild(m.childNodes[0]);
2056
- }
2057
- m.appendChild(p);
2058
- m = m.parentNode;
2059
- }
2060
- m.parentNode.appendChild(a);
2061
- }
2062
- anchorNode = anchorNode.cloneNode(false);
2063
- }
2064
-
2065
- if (!dom.check.isZeroWidth(prevNode)) {
2066
- ancestor.appendChild(prevNode);
2067
- }
2068
-
2069
- const prevAnchorNode = _getMaintainedNode(ancestor);
2070
- if (prevAnchorNode) anchorNode = prevAnchorNode;
2071
- if (anchorNode) line = anchorNode;
2072
-
2073
- newNode = /** @type {HTMLElement} */ (child);
2074
- pCurrent = [];
2075
- cssText = '';
2076
- while (newNode !== line && newNode !== el && newNode !== null) {
2077
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
2078
- if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
2079
- pCurrent.push(vNode);
2080
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2081
- }
2082
- newNode = newNode.parentElement;
2083
- }
2084
-
2085
- const childNode = pCurrent.pop() || textNode;
2086
- appendNode = newNode = childNode;
2087
- while (pCurrent.length > 0) {
2088
- newNode = pCurrent.pop();
2089
- appendNode.appendChild(newNode);
2090
- appendNode = newNode;
2091
- }
2092
-
2093
- newInnerNode.appendChild(childNode);
2094
- line.appendChild(newInnerNode);
2095
-
2096
- if (anchorNode && !_getMaintainedNode(endContainer)) {
2097
- newInnerNode = newInnerNode.cloneNode(false);
2098
- pNode.appendChild(newInnerNode);
2099
- nNodeArray.push(newInnerNode);
2100
- }
2101
-
2102
- startContainer = textNode;
2103
- startOffset = 0;
2104
- startPass = true;
2105
-
2106
- if (newNode !== textNode) newNode.appendChild(startContainer);
2107
- if (!isSameNode) continue;
2108
- }
2109
-
2110
- // endContainer
2111
- if (!endPass && child === endContainer) {
2112
- anchorNode = _getMaintainedNode(child);
2113
-
2114
- let _prevText = '';
2115
- let _nextText = '';
2116
- if (endContainer.nodeType === 3) {
2117
- const eText = /** @type {Text} */ (endContainer);
2118
- _prevText = eText.substringData(endOffset, eText.length - endOffset);
2119
- _nextText = isSameNode ? '' : eText.substringData(0, endOffset);
2120
- }
2121
-
2122
- const afterNode = dom.utils.createTextNode(_prevText);
2123
- const textNode = dom.utils.createTextNode(_nextText);
2124
-
2125
- if (anchorNode) {
2126
- anchorNode = anchorNode.cloneNode(false);
2127
- } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
2128
- newInnerNode = newInnerNode.cloneNode(false);
2129
- pNode.appendChild(newInnerNode);
2130
- nNodeArray.push(newInnerNode);
2131
- }
2132
-
2133
- if (!dom.check.isZeroWidth(afterNode)) {
2134
- newNode = /** @type {HTMLElement} */ (child);
2135
- cssText = '';
2136
- pCurrent = [];
2137
- const anchors = [];
2138
- while (newNode !== pNode && newNode !== el && newNode !== null) {
2139
- if (newNode.nodeType === 1 && checkCss(newNode)) {
2140
- if (_isMaintainedNode(newNode)) anchors.push(newNode.cloneNode(false));
2141
- else pCurrent.push(newNode.cloneNode(false));
2142
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2143
- }
2144
- newNode = newNode.parentElement;
2145
- }
2146
- pCurrent = pCurrent.concat(anchors);
2147
-
2148
- cloneNode = appendNode = newNode = pCurrent.pop() || afterNode;
2149
- while (pCurrent.length > 0) {
2150
- newNode = pCurrent.pop();
2151
- appendNode.appendChild(newNode);
2152
- appendNode = newNode;
2153
- }
2154
-
2155
- pNode.appendChild(cloneNode);
2156
- newNode.textContent = afterNode.data;
2157
- }
2158
-
2159
- if (anchorNode && cloneNode) {
2160
- const afterAnchorNode = _getMaintainedNode(cloneNode);
2161
- if (afterAnchorNode) {
2162
- anchorNode = afterAnchorNode;
2163
- }
2164
- }
2165
-
2166
- newNode = /** @type {HTMLElement} */ (child);
2167
- pCurrent = [];
2168
- cssText = '';
2169
- while (newNode !== pNode && newNode !== el && newNode !== null) {
2170
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
2171
- if (vNode && newNode.nodeType === 1 && checkCss(newNode)) {
2172
- pCurrent.push(vNode);
2173
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2174
- }
2175
- newNode = newNode.parentElement;
2176
- }
2177
-
2178
- const childNode = pCurrent.pop() || textNode;
2179
- appendNode = newNode = childNode;
2180
- while (pCurrent.length > 0) {
2181
- newNode = pCurrent.pop();
2182
- appendNode.appendChild(newNode);
2183
- appendNode = newNode;
2184
- }
2185
-
2186
- if (anchorNode) {
2187
- newInnerNode = newInnerNode.cloneNode(false);
2188
- newInnerNode.appendChild(childNode);
2189
- anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
2190
- pNode.appendChild(anchorNode);
2191
- nNodeArray.push(newInnerNode);
2192
- anchorNode = null;
2193
- } else {
2194
- newInnerNode.appendChild(childNode);
2195
- }
2196
-
2197
- endContainer = textNode;
2198
- endOffset = textNode.data.length;
2199
- endPass = true;
2200
-
2201
- if (!isRemoveFormat && collapsed) {
2202
- newInnerNode = textNode;
2203
- textNode.textContent = unicode.zeroWidthSpace;
2204
- }
2205
-
2206
- if (newNode !== textNode) newNode.appendChild(endContainer);
2207
- continue;
2208
- }
2209
-
2210
- // other
2211
- if (startPass) {
2212
- if (child.nodeType === 1 && !dom.check.isBreak(child)) {
2213
- if (inst._isIgnoreNodeChange(child)) {
2214
- pNode.appendChild(child.cloneNode(true));
2215
- if (!collapsed) {
2216
- newInnerNode = newInnerNode.cloneNode(false);
2217
- pNode.appendChild(newInnerNode);
2218
- nNodeArray.push(newInnerNode);
2219
- }
2220
- } else {
2221
- recursionFunc(child, child);
2222
- }
2223
- continue;
2224
- }
2225
-
2226
- newNode = /** @type {HTMLElement} */ (child);
2227
- pCurrent = [];
2228
- cssText = '';
2229
- const anchors = [];
2230
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2231
- vNode = endPass ? newNode.cloneNode(false) : validation(newNode);
2232
- if (newNode.nodeType === 1 && !dom.check.isBreak(child) && vNode && checkCss(newNode)) {
2233
- if (_isMaintainedNode(newNode)) {
2234
- if (!anchorNode) anchors.push(vNode);
2235
- } else {
2236
- pCurrent.push(vNode);
2237
- }
2238
- cssText += newNode.style.cssText.substring(0, newNode.style.cssText.indexOf(':')) + '|';
2239
- }
2240
- newNode = newNode.parentElement;
2241
- }
2242
- pCurrent = pCurrent.concat(anchors);
2243
-
2244
- const childNode = pCurrent.pop() || child;
2245
- appendNode = newNode = childNode;
2246
- while (pCurrent.length > 0) {
2247
- newNode = pCurrent.pop();
2248
- appendNode.appendChild(newNode);
2249
- appendNode = newNode;
2250
- }
2251
-
2252
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode) && !dom.check.isZeroWidth(newInnerNode)) {
2253
- newInnerNode = newInnerNode.cloneNode(false);
2254
- pNode.appendChild(newInnerNode);
2255
- nNodeArray.push(newInnerNode);
2256
- }
2257
-
2258
- if (!endPass && !anchorNode && _isMaintainedNode(childNode)) {
2259
- newInnerNode = newInnerNode.cloneNode(false);
2260
- const aChildren = childNode.childNodes;
2261
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2262
- newInnerNode.appendChild(aChildren[a]);
2263
- }
2264
- childNode.appendChild(newInnerNode);
2265
- pNode.appendChild(childNode);
2266
- nNodeArray.push(newInnerNode);
2267
- if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
2268
- else ancestor = newInnerNode;
2269
- } else if (childNode === child) {
2270
- if (!endPass) ancestor = newInnerNode;
2271
- else ancestor = pNode;
2272
- } else if (endPass) {
2273
- pNode.appendChild(childNode);
2274
- ancestor = newNode;
2275
- } else {
2276
- newInnerNode.appendChild(childNode);
2277
- ancestor = newNode;
2278
- }
2279
-
2280
- if (anchorNode && child.nodeType === 3) {
2281
- if (_getMaintainedNode(child)) {
2282
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2283
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2284
- });
2285
- anchorNode.appendChild(ancestorAnchorNode);
2286
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2287
- nNodeArray.push(newInnerNode);
2288
- pNode.appendChild(newInnerNode);
2289
- } else {
2290
- anchorNode = null;
2291
- }
2292
- }
2293
- }
2294
-
2295
- cloneNode = child.cloneNode(false);
2296
- ancestor.appendChild(cloneNode);
2297
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = cloneNode;
2298
-
2299
- recursionFunc(child, coverNode);
2300
- }
2301
- })(element, pNode);
2302
-
2303
- // not remove tag
2304
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
2305
- return {
2306
- ancestor: element,
2307
- startContainer: startCon,
2308
- startOffset: startOff,
2309
- endContainer: endCon,
2310
- endOffset: endOff
2311
- };
2312
- }
2313
-
2314
- isRemoveFormat = isRemoveFormat && isRemoveNode;
2315
-
2316
- if (isRemoveFormat) {
2317
- for (let i = 0; i < nNodeArray.length; i++) {
2318
- const removeNode = nNodeArray[i];
2319
- let textNode, textNode_s, textNode_e;
2320
-
2321
- if (collapsed) {
2322
- textNode = dom.utils.createTextNode(unicode.zeroWidthSpace);
2323
- pNode.replaceChild(textNode, removeNode);
2324
- } else {
2325
- const rChildren = removeNode.childNodes;
2326
- textNode_s = rChildren[0];
2327
- while (rChildren[0]) {
2328
- textNode_e = rChildren[0];
2329
- pNode.insertBefore(textNode_e, removeNode);
2330
- }
2331
- dom.utils.removeItem(removeNode);
2332
- }
2333
-
2334
- if (i === 0) {
2335
- if (collapsed) {
2336
- startContainer = endContainer = textNode;
2337
- } else {
2338
- startContainer = textNode_s;
2339
- endContainer = textNode_e;
2340
- }
2341
- }
2342
- }
2343
- } else {
2344
- if (isRemoveNode) {
2345
- for (let i = 0; i < nNodeArray.length; i++) {
2346
- SN_StripRemoveNode(nNodeArray[i]);
2347
- }
2348
- }
2349
-
2350
- if (collapsed) {
2351
- startContainer = endContainer = newInnerNode;
2352
- }
2353
- }
2354
-
2355
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2356
-
2357
- if (collapsed) {
2358
- startOffset = startContainer.textContent.length;
2359
- endOffset = endContainer.textContent.length;
2360
- }
2361
-
2362
- // endContainer reset
2363
- const endConReset = isRemoveFormat || endContainer.textContent.length === 0;
2364
-
2365
- if (!dom.check.isBreak(endContainer) && endContainer.textContent.length === 0) {
2366
- dom.utils.removeItem(endContainer);
2367
- endContainer = startContainer;
2368
- }
2369
- endOffset = endConReset ? endContainer.textContent.length : endOffset;
2370
-
2371
- // node change
2372
- const newStartOffset = {
2373
- s: 0,
2374
- e: 0
2375
- };
2376
- const startPath = dom.query.getNodePath(startContainer, pNode, newStartOffset);
2377
-
2378
- const mergeEndCon = !endContainer.parentNode;
2379
- if (mergeEndCon) endContainer = startContainer;
2380
- const newEndOffset = {
2381
- s: 0,
2382
- e: 0
2383
- };
2384
- const endPath = dom.query.getNodePath(endContainer, pNode, !mergeEndCon && !endConReset ? newEndOffset : null);
2385
-
2386
- startOffset += newStartOffset.s;
2387
- endOffset = collapsed ? startOffset : mergeEndCon ? startContainer.textContent.length : endConReset ? endOffset + newStartOffset.s : endOffset + newEndOffset.s;
2388
-
2389
- // tag merge
2390
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [startPath, endPath], true);
2391
-
2392
- element.parentNode.replaceChild(pNode, element);
2393
-
2394
- startContainer = dom.query.getNodeFromPath(startPath, pNode);
2395
- endContainer = dom.query.getNodeFromPath(endPath, pNode);
2396
-
2397
- return {
2398
- ancestor: pNode,
2399
- startContainer: startContainer,
2400
- startOffset: startOffset + newOffsets[0],
2401
- endContainer: endContainer,
2402
- endOffset: endOffset + newOffsets[1]
2403
- };
2404
- },
2405
-
2406
- /**
2407
- * @private
2408
- * @this {FormatThis}
2409
- * @description wraps first line selected text.
2410
- * @param {Node} element The node of the line that contains the selected text node.
2411
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2412
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2413
- * @param {Node} startCon The startContainer property of the selection object.
2414
- * @param {number} startOff The startOffset property of the selection object.
2415
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2416
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2417
- * @returns {NodeStyleContainerType} { ancestor, container, offset, endContainer }
2418
- */
2419
- _setNode_startLine(element, newInnerNode, validation, startCon, startOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode, _endContainer) {
2420
- // not add tag
2421
- let parentCon = startCon.parentNode;
2422
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
2423
- if (parentCon.nodeName === newInnerNode.nodeName) break;
2424
- parentCon = parentCon.parentNode;
2425
- }
2426
-
2427
- if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.nextSibling && dom.check.isZeroWidth(startCon.textContent.slice(0, startOff))) {
2428
- let sameTag = false;
2429
- let s = startCon.previousSibling;
2430
- while (s) {
2431
- if (!dom.check.isZeroWidth(s)) {
2432
- sameTag = false;
2433
- break;
2434
- }
2435
- s = s.previousSibling;
2436
- }
2437
-
2438
- if (sameTag) {
2439
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
2440
-
2441
- return {
2442
- ancestor: element,
2443
- container: startCon,
2444
- offset: startOff
2445
- };
2446
- }
2447
- }
2448
-
2449
- // add tag
2450
- _removeCheck.v = false;
2451
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2452
- const inst = this;
2453
- const el = element;
2454
- const nNodeArray = [newInnerNode];
2455
- const pNode = element.cloneNode(false);
2456
-
2457
- let container = startCon;
2458
- let offset = startOff;
2459
- let passNode = false;
2460
- let pCurrent, newNode, appendNode, anchorNode;
2461
-
2462
- (function recursionFunc(current, ancestor) {
2463
- const childNodes = current.childNodes;
2464
-
2465
- for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
2466
- const child = /** @type {HTMLElement} */ (childNodes[i]);
2467
- if (!child) continue;
2468
- let coverNode = ancestor;
2469
-
2470
- if (passNode && !dom.check.isBreak(child)) {
2471
- if (child.nodeType === 1) {
2472
- if (inst._isIgnoreNodeChange(child)) {
2473
- newInnerNode = newInnerNode.cloneNode(false);
2474
- cloneChild = child.cloneNode(true);
2475
- pNode.appendChild(cloneChild);
2476
- pNode.appendChild(newInnerNode);
2477
- nNodeArray.push(newInnerNode);
2478
-
2479
- // end container
2480
- if (_endContainer && child.contains(_endContainer)) {
2481
- const endPath = dom.query.getNodePath(_endContainer, child);
2482
- _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
2483
- }
2484
- } else {
2485
- recursionFunc(child, child);
2486
- }
2487
- continue;
2488
- }
2489
-
2490
- newNode = child;
2491
- pCurrent = [];
2492
- const anchors = [];
2493
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2494
- vNode = validation(newNode);
2495
- if (newNode.nodeType === 1 && vNode) {
2496
- if (_isMaintainedNode(newNode)) {
2497
- if (!anchorNode) anchors.push(vNode);
2498
- } else {
2499
- pCurrent.push(vNode);
2500
- }
2501
- }
2502
- newNode = newNode.parentNode;
2503
- }
2504
- pCurrent = pCurrent.concat(anchors);
2505
-
2506
- const isTopNode = pCurrent.length > 0;
2507
- const childNode = pCurrent.pop() || child;
2508
- appendNode = newNode = childNode;
2509
- while (pCurrent.length > 0) {
2510
- newNode = pCurrent.pop();
2511
- appendNode.appendChild(newNode);
2512
- appendNode = newNode;
2513
- }
2514
-
2515
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
2516
- newInnerNode = newInnerNode.cloneNode(false);
2517
- pNode.appendChild(newInnerNode);
2518
- nNodeArray.push(newInnerNode);
2519
- }
2520
-
2521
- if (!anchorNode && _isMaintainedNode(childNode)) {
2522
- newInnerNode = newInnerNode.cloneNode(false);
2523
- const aChildren = childNode.childNodes;
2524
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2525
- newInnerNode.appendChild(aChildren[a]);
2526
- }
2527
- childNode.appendChild(newInnerNode);
2528
- pNode.appendChild(childNode);
2529
- ancestor = !_isMaintainedNode(newNode) ? newNode : newInnerNode;
2530
- nNodeArray.push(newInnerNode);
2531
- } else if (isTopNode) {
2532
- newInnerNode.appendChild(childNode);
2533
- ancestor = newNode;
2534
- } else {
2535
- ancestor = newInnerNode;
2536
- }
2537
-
2538
- if (anchorNode && child.nodeType === 3) {
2539
- if (_getMaintainedNode(child)) {
2540
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2541
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2542
- });
2543
- anchorNode.appendChild(ancestorAnchorNode);
2544
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2545
- nNodeArray.push(newInnerNode);
2546
- pNode.appendChild(newInnerNode);
2547
- } else {
2548
- anchorNode = null;
2549
- }
2550
- }
2551
- }
2552
-
2553
- // startContainer
2554
- if (!passNode && child === container) {
2555
- let line = pNode;
2556
- anchorNode = _getMaintainedNode(child);
2557
-
2558
- let _prevText = '';
2559
- let _nextText = '';
2560
- if (container.nodeType === 3) {
2561
- const cText = /** @type {Text} */ (container);
2562
- _prevText = cText.substringData(0, offset);
2563
- _nextText = cText.substringData(offset, cText.length - offset);
2564
- }
2565
-
2566
- const prevNode = dom.utils.createTextNode(_prevText);
2567
- const textNode = dom.utils.createTextNode(_nextText);
2568
-
2569
- if (anchorNode) {
2570
- const a = _getMaintainedNode(ancestor);
2571
- if (a && a.parentNode !== line) {
2572
- let m = a;
2573
- let p = null;
2574
- while (m.parentNode !== line) {
2575
- ancestor = p = m.parentNode.cloneNode(false);
2576
- while (m.childNodes[0]) {
2577
- p.appendChild(m.childNodes[0]);
2578
- }
2579
- m.appendChild(p);
2580
- m = m.parentNode;
2581
- }
2582
- m.parentNode.appendChild(a);
2583
- }
2584
- anchorNode = anchorNode.cloneNode(false);
2585
- }
2586
-
2587
- if (!dom.check.isZeroWidth(prevNode)) {
2588
- ancestor.appendChild(prevNode);
2589
- }
2590
-
2591
- const prevAnchorNode = _getMaintainedNode(ancestor);
2592
- if (prevAnchorNode) anchorNode = prevAnchorNode;
2593
- if (anchorNode) line = anchorNode;
2594
-
2595
- newNode = ancestor;
2596
- pCurrent = [];
2597
- while (newNode !== line && newNode !== null) {
2598
- vNode = validation(newNode);
2599
- if (newNode.nodeType === 1 && vNode) {
2600
- pCurrent.push(vNode);
2601
- }
2602
- newNode = newNode.parentNode;
2603
- }
2604
-
2605
- const childNode = pCurrent.pop() || ancestor;
2606
- appendNode = newNode = childNode;
2607
- while (pCurrent.length > 0) {
2608
- newNode = pCurrent.pop();
2609
- appendNode.appendChild(newNode);
2610
- appendNode = newNode;
2611
- }
2612
-
2613
- if (childNode !== ancestor) {
2614
- newInnerNode.appendChild(childNode);
2615
- ancestor = newNode;
2616
- } else {
2617
- ancestor = newInnerNode;
2618
- }
2619
-
2620
- if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
2621
- line.appendChild(newInnerNode);
2622
-
2623
- container = textNode;
2624
- offset = 0;
2625
- passNode = true;
2626
-
2627
- ancestor.appendChild(container);
2628
- continue;
2629
- }
2630
-
2631
- vNode = !passNode ? child.cloneNode(false) : validation(child);
2632
- if (vNode) {
2633
- ancestor.appendChild(vNode);
2634
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
2635
- }
2636
-
2637
- recursionFunc(child, coverNode);
2638
- }
2639
- })(element, pNode);
2640
-
2641
- // not remove tag
2642
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
2643
- return {
2644
- ancestor: element,
2645
- container: startCon,
2646
- offset: startOff,
2647
- endContainer: _endContainer
2648
- };
2649
- }
2650
-
2651
- isRemoveFormat = isRemoveFormat && isRemoveNode;
2652
-
2653
- if (isRemoveFormat) {
2654
- for (let i = 0; i < nNodeArray.length; i++) {
2655
- const removeNode = nNodeArray[i];
2656
-
2657
- const rChildren = removeNode.childNodes;
2658
- const textNode = rChildren[0];
2659
- while (rChildren[0]) {
2660
- pNode.insertBefore(rChildren[0], removeNode);
2661
- }
2662
- dom.utils.removeItem(removeNode);
2663
-
2664
- if (i === 0) container = textNode;
2665
- }
2666
- } else if (isRemoveNode) {
2667
- newInnerNode = newInnerNode.firstChild;
2668
- for (let i = 0; i < nNodeArray.length; i++) {
2669
- SN_StripRemoveNode(nNodeArray[i]);
2670
- }
2671
- }
2672
-
2673
- if (!isRemoveFormat && pNode.childNodes.length === 0) {
2674
- if (element.childNodes) {
2675
- container = element.childNodes[0];
2676
- } else {
2677
- container = dom.utils.createTextNode(unicode.zeroWidthSpace);
2678
- element.appendChild(container);
2679
- }
2680
- } else {
2681
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2682
-
2683
- if (dom.check.isZeroWidth(pNode.textContent)) {
2684
- container = pNode.firstChild;
2685
- offset = 0;
2686
- }
2687
-
2688
- // node change
2689
- const offsets = {
2690
- s: 0,
2691
- e: 0
2692
- };
2693
- const path = dom.query.getNodePath(container, pNode, offsets);
2694
- offset += offsets.s;
2695
-
2696
- // tag merge
2697
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
2698
-
2699
- element.parentNode.replaceChild(pNode, element);
2700
-
2701
- container = dom.query.getNodeFromPath(path, pNode);
2702
- offset += newOffsets[0];
2703
- }
2704
-
2705
- return {
2706
- ancestor: pNode,
2707
- container: container,
2708
- offset: offset,
2709
- endContainer: _endContainer
2710
- };
2711
- },
2712
-
2713
- /**
2714
- * @private
2715
- * @this {FormatThis}
2716
- * @description wraps mid lines selected text.
2717
- * @param {HTMLElement} element The node of the line that contains the selected text node.
2718
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2719
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2720
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2721
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2722
- * @param {Node} _endContainer Offset node of last line already modified (end.container)
2723
- * @returns {NodeStyleContainerType} { ancestor, endContainer: "If end container is renewed, returned renewed node" }
2724
- */
2725
- _setNode_middleLine(element, newInnerNode, validation, isRemoveFormat, isRemoveNode, _removeCheck, _endContainer) {
2726
- // not add tag
2727
- if (!isRemoveNode) {
2728
- // end container path
2729
- let endPath = null;
2730
- if (_endContainer && element.contains(_endContainer)) endPath = dom.query.getNodePath(_endContainer, element);
2731
-
2732
- const tempNode = element.cloneNode(true);
2733
- const newNodeName = /** @type {HTMLElement} */ (newInnerNode).nodeName;
2734
- const newCssText = /** @type {HTMLElement} */ (newInnerNode).style.cssText;
2735
- const newClass = /** @type {HTMLElement} */ (newInnerNode).className;
2736
-
2737
- let children = tempNode.childNodes;
2738
- let i = 0,
2739
- len = children.length;
2740
- for (let child; i < len; i++) {
2741
- child = /** @type {HTMLElement} */ (children[i]);
2742
- if (child.nodeType === 3) break;
2743
- if (child.nodeName === newNodeName) {
2744
- child.style.cssText += newCssText;
2745
- dom.utils.addClass(child, newClass);
2746
- } else if (!dom.check.isBreak(child) && this._isIgnoreNodeChange(child)) {
2747
- continue;
2748
- } else if (len === 1) {
2749
- children = child.childNodes;
2750
- len = children.length;
2751
- i = -1;
2752
- continue;
2753
- } else {
2754
- break;
2755
- }
2756
- }
2757
-
2758
- if (len > 0 && i === len) {
2759
- element.innerHTML = /** @type {HTMLElement} */ (tempNode).innerHTML;
2760
- return {
2761
- ancestor: element,
2762
- endContainer: endPath ? dom.query.getNodeFromPath(endPath, element) : null
2763
- };
2764
- }
2765
- }
2766
-
2767
- // add tag
2768
- _removeCheck.v = false;
2769
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2770
- const inst = this;
2771
- const pNode = element.cloneNode(false);
2772
- const nNodeArray = [newInnerNode];
2773
- let noneChange = true;
2774
-
2775
- (function recursionFunc(current, ancestor) {
2776
- const childNodes = current.childNodes;
2777
-
2778
- for (let i = 0, len = childNodes.length, vNode, cloneChild; i < len; i++) {
2779
- const child = /** @type {HTMLElement} */ (childNodes[i]);
2780
- if (!child) continue;
2781
- let coverNode = ancestor;
2782
-
2783
- if (!dom.check.isBreak(child) && inst._isIgnoreNodeChange(child)) {
2784
- if (newInnerNode.childNodes.length > 0) {
2785
- pNode.appendChild(newInnerNode);
2786
- newInnerNode = newInnerNode.cloneNode(false);
2787
- }
2788
-
2789
- cloneChild = child.cloneNode(true);
2790
- pNode.appendChild(cloneChild);
2791
- pNode.appendChild(newInnerNode);
2792
- nNodeArray.push(newInnerNode);
2793
- ancestor = newInnerNode;
2794
-
2795
- // end container
2796
- if (_endContainer && child.contains(_endContainer)) {
2797
- const endPath = dom.query.getNodePath(_endContainer, child);
2798
- _endContainer = dom.query.getNodeFromPath(endPath, cloneChild);
2799
- }
2800
-
2801
- continue;
2802
- } else {
2803
- vNode = validation(child);
2804
- if (vNode) {
2805
- noneChange = false;
2806
- ancestor.appendChild(vNode);
2807
- if (child.nodeType === 1) coverNode = vNode;
2808
- }
2809
- }
2810
-
2811
- if (!dom.check.isBreak(child)) recursionFunc(child, coverNode);
2812
- }
2813
- })(element, newInnerNode);
2814
-
2815
- // not remove tag
2816
- if (noneChange || (isRemoveNode && !isRemoveFormat && !_removeCheck.v))
2817
- return {
2818
- ancestor: element,
2819
- endContainer: _endContainer
2820
- };
2821
-
2822
- pNode.appendChild(newInnerNode);
2823
-
2824
- if (isRemoveFormat && isRemoveNode) {
2825
- for (let i = 0; i < nNodeArray.length; i++) {
2826
- const removeNode = nNodeArray[i];
2827
-
2828
- const rChildren = removeNode.childNodes;
2829
- while (rChildren[0]) {
2830
- pNode.insertBefore(rChildren[0], removeNode);
2831
- }
2832
- dom.utils.removeItem(removeNode);
2833
- }
2834
- } else if (isRemoveNode) {
2835
- newInnerNode = newInnerNode.firstChild;
2836
- for (let i = 0; i < nNodeArray.length; i++) {
2837
- SN_StripRemoveNode(nNodeArray[i]);
2838
- }
2839
- }
2840
-
2841
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
2842
- this.nodeTransform.mergeSameTags(pNode, null, true);
2843
-
2844
- // node change
2845
- element.parentNode.replaceChild(pNode, element);
2846
- return {
2847
- ancestor: pNode,
2848
- endContainer: _endContainer
2849
- };
2850
- },
2851
-
2852
- /**
2853
- * @private
2854
- * @this {FormatThis}
2855
- * @description wraps last line selected text.
2856
- * @param {Node} element The node of the line that contains the selected text node.
2857
- * @param {Node} newInnerNode The dom that will wrap the selected text area
2858
- * @param {(current: Node) => Node|null} validation Check if the node should be stripped.
2859
- * @param {Node} endCon The endContainer property of the selection object.
2860
- * @param {number} endOff The endOffset property of the selection object.
2861
- * @param {boolean} isRemoveFormat Is the remove all formats command?
2862
- * @param {boolean} isRemoveNode "newInnerNode" is remove node?
2863
- * @returns {NodeStyleContainerType} { ancestor, container, offset }
2864
- */
2865
- _setNode_endLine(element, newInnerNode, validation, endCon, endOff, isRemoveFormat, isRemoveNode, _removeCheck, _getMaintainedNode, _isMaintainedNode) {
2866
- // not add tag
2867
- let parentCon = endCon.parentNode;
2868
- while (!parentCon.nextSibling && !parentCon.previousSibling && !this.isLine(parentCon.parentNode) && !dom.check.isWysiwygFrame(parentCon.parentNode)) {
2869
- if (parentCon.nodeName === newInnerNode.nodeName) break;
2870
- parentCon = parentCon.parentNode;
2871
- }
2872
-
2873
- if (!isRemoveNode && parentCon.nodeName === newInnerNode.nodeName && !this.isLine(parentCon) && !parentCon.previousSibling && dom.check.isZeroWidth(endCon.textContent.slice(endOff))) {
2874
- let sameTag = false;
2875
- let e = endCon.nextSibling;
2876
- while (e) {
2877
- if (!dom.check.isZeroWidth(e)) {
2878
- sameTag = false;
2879
- break;
2880
- }
2881
- e = e.nextSibling;
2882
- }
2883
-
2884
- if (sameTag) {
2885
- dom.utils.copyTagAttributes(parentCon, newInnerNode);
2886
-
2887
- return {
2888
- ancestor: element,
2889
- container: endCon,
2890
- offset: endOff
2891
- };
2892
- }
2893
- }
2894
-
2895
- // add tag
2896
- _removeCheck.v = false;
2897
- // eslint-disable-next-line @typescript-eslint/no-this-alias
2898
- const inst = this;
2899
- const el = element;
2900
- const nNodeArray = [newInnerNode];
2901
- const pNode = element.cloneNode(false);
2902
-
2903
- let container = endCon;
2904
- let offset = endOff;
2905
- let passNode = false;
2906
- let pCurrent, newNode, appendNode, anchorNode;
2907
-
2908
- (function recursionFunc(current, ancestor) {
2909
- const childNodes = current.childNodes;
2910
-
2911
- for (let i = childNodes.length - 1, vNode; 0 <= i; i--) {
2912
- const child = childNodes[i];
2913
- if (!child) continue;
2914
- let coverNode = ancestor;
2915
-
2916
- if (passNode && !dom.check.isBreak(child)) {
2917
- if (child.nodeType === 1) {
2918
- if (inst._isIgnoreNodeChange(child)) {
2919
- newInnerNode = newInnerNode.cloneNode(false);
2920
- const cloneChild = child.cloneNode(true);
2921
- pNode.insertBefore(cloneChild, ancestor);
2922
- pNode.insertBefore(newInnerNode, cloneChild);
2923
- nNodeArray.push(newInnerNode);
2924
- } else {
2925
- recursionFunc(child, child);
2926
- }
2927
- continue;
2928
- }
2929
-
2930
- newNode = child;
2931
- pCurrent = [];
2932
- const anchors = [];
2933
- while (newNode.parentNode !== null && newNode !== el && newNode !== newInnerNode) {
2934
- vNode = validation(newNode);
2935
- if (vNode && newNode.nodeType === 1) {
2936
- if (_isMaintainedNode(newNode)) {
2937
- if (!anchorNode) anchors.push(vNode);
2938
- } else {
2939
- pCurrent.push(vNode);
2940
- }
2941
- }
2942
- newNode = newNode.parentNode;
2943
- }
2944
- pCurrent = pCurrent.concat(anchors);
2945
-
2946
- const isTopNode = pCurrent.length > 0;
2947
- const childNode = pCurrent.pop() || child;
2948
- appendNode = newNode = childNode;
2949
- while (pCurrent.length > 0) {
2950
- newNode = pCurrent.pop();
2951
- appendNode.appendChild(newNode);
2952
- appendNode = newNode;
2953
- }
2954
-
2955
- if (_isMaintainedNode(newInnerNode.parentNode) && !_isMaintainedNode(childNode)) {
2956
- newInnerNode = newInnerNode.cloneNode(false);
2957
- pNode.insertBefore(newInnerNode, pNode.firstChild);
2958
- nNodeArray.push(newInnerNode);
2959
- }
2960
-
2961
- if (!anchorNode && _isMaintainedNode(childNode)) {
2962
- newInnerNode = newInnerNode.cloneNode(false);
2963
- const aChildren = childNode.childNodes;
2964
- for (let a = 0, aLen = aChildren.length; a < aLen; a++) {
2965
- newInnerNode.appendChild(aChildren[a]);
2966
- }
2967
- childNode.appendChild(newInnerNode);
2968
- pNode.insertBefore(childNode, pNode.firstChild);
2969
- nNodeArray.push(newInnerNode);
2970
- if (/** @type {HTMLElement} */ (newInnerNode).children.length > 0) ancestor = newNode;
2971
- else ancestor = newInnerNode;
2972
- } else if (isTopNode) {
2973
- newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
2974
- ancestor = newNode;
2975
- } else {
2976
- ancestor = newInnerNode;
2977
- }
2978
-
2979
- if (anchorNode && child.nodeType === 3) {
2980
- if (_getMaintainedNode(child)) {
2981
- const ancestorAnchorNode = dom.query.getParentElement(ancestor, (c) => {
2982
- return inst._isNonSplitNode(c.parentNode) || c.parentNode === pNode;
2983
- });
2984
- anchorNode.appendChild(ancestorAnchorNode);
2985
- newInnerNode = ancestorAnchorNode.cloneNode(false);
2986
- nNodeArray.push(newInnerNode);
2987
- pNode.insertBefore(newInnerNode, pNode.firstChild);
2988
- } else {
2989
- anchorNode = null;
2990
- }
2991
- }
2992
- }
2993
-
2994
- // endContainer
2995
- if (!passNode && child === container) {
2996
- anchorNode = _getMaintainedNode(child);
2997
-
2998
- let _prevText = '';
2999
- let _nextText = '';
3000
- if (container.nodeType === 3) {
3001
- const cText = /** @type {Text} */ (container);
3002
- _prevText = cText.substringData(offset, cText.length - offset);
3003
- _nextText = cText.substringData(0, offset);
3004
- }
3005
-
3006
- const afterNode = dom.utils.createTextNode(_prevText);
3007
- const textNode = dom.utils.createTextNode(_nextText);
3008
-
3009
- if (anchorNode) {
3010
- anchorNode = anchorNode.cloneNode(false);
3011
- const a = _getMaintainedNode(ancestor);
3012
- if (a.parentNode !== pNode) {
3013
- let m = a;
3014
- let p = null;
3015
- while (m.parentNode !== pNode) {
3016
- ancestor = p = m.parentNode.cloneNode(false);
3017
- while (m.childNodes[0]) {
3018
- p.appendChild(m.childNodes[0]);
3019
- }
3020
- m.appendChild(p);
3021
- m = m.parentNode;
3022
- }
3023
- m.parentNode.insertBefore(a, m.parentNode.firstChild);
3024
- }
3025
- anchorNode = anchorNode.cloneNode(false);
3026
- } else if (_isMaintainedNode(newInnerNode.parentNode) && !anchorNode) {
3027
- newInnerNode = newInnerNode.cloneNode(false);
3028
- pNode.appendChild(newInnerNode);
3029
- nNodeArray.push(newInnerNode);
3030
- }
3031
-
3032
- if (!dom.check.isZeroWidth(afterNode)) {
3033
- ancestor.insertBefore(afterNode, ancestor.firstChild);
3034
- }
3035
-
3036
- newNode = ancestor;
3037
- pCurrent = [];
3038
- while (newNode !== pNode && newNode !== null) {
3039
- vNode = _isMaintainedNode(newNode) ? null : validation(newNode);
3040
- if (vNode && newNode.nodeType === 1) {
3041
- pCurrent.push(vNode);
3042
- }
3043
- newNode = newNode.parentNode;
3044
- }
3045
-
3046
- const childNode = pCurrent.pop() || ancestor;
3047
- appendNode = newNode = childNode;
3048
- while (pCurrent.length > 0) {
3049
- newNode = pCurrent.pop();
3050
- appendNode.appendChild(newNode);
3051
- appendNode = newNode;
3052
- }
3053
-
3054
- if (childNode !== ancestor) {
3055
- newInnerNode.insertBefore(childNode, newInnerNode.firstChild);
3056
- ancestor = newNode;
3057
- } else {
3058
- ancestor = newInnerNode;
3059
- }
3060
-
3061
- if (dom.check.isBreak(child)) newInnerNode.appendChild(child.cloneNode(false));
3062
-
3063
- if (anchorNode) {
3064
- anchorNode.insertBefore(newInnerNode, anchorNode.firstChild);
3065
- pNode.insertBefore(anchorNode, pNode.firstChild);
3066
- anchorNode = null;
3067
- } else {
3068
- pNode.insertBefore(newInnerNode, pNode.firstChild);
3069
- }
3070
-
3071
- container = textNode;
3072
- offset = textNode.data.length;
3073
- passNode = true;
3074
-
3075
- ancestor.insertBefore(container, ancestor.firstChild);
3076
- continue;
3077
- }
3078
-
3079
- vNode = !passNode ? child.cloneNode(false) : validation(child);
3080
- if (vNode) {
3081
- ancestor.insertBefore(vNode, ancestor.firstChild);
3082
- if (child.nodeType === 1 && !dom.check.isBreak(child)) coverNode = vNode;
3083
- }
3084
-
3085
- recursionFunc(child, coverNode);
3086
- }
3087
- })(element, pNode);
3088
-
3089
- // not remove tag
3090
- if (isRemoveNode && !isRemoveFormat && !_removeCheck.v) {
3091
- return {
3092
- ancestor: element,
3093
- container: endCon,
3094
- offset: endOff
3095
- };
3096
- }
3097
-
3098
- isRemoveFormat = isRemoveFormat && isRemoveNode;
3099
-
3100
- if (isRemoveFormat) {
3101
- for (let i = 0; i < nNodeArray.length; i++) {
3102
- const removeNode = nNodeArray[i];
3103
-
3104
- const rChildren = removeNode.childNodes;
3105
- let textNode = null;
3106
- while (rChildren[0]) {
3107
- textNode = rChildren[0];
3108
- pNode.insertBefore(textNode, removeNode);
3109
- }
3110
- dom.utils.removeItem(removeNode);
3111
-
3112
- if (i === nNodeArray.length - 1) {
3113
- container = textNode;
3114
- offset = textNode.textContent.length;
3115
- }
3116
- }
3117
- } else if (isRemoveNode) {
3118
- newInnerNode = newInnerNode.firstChild;
3119
- for (let i = 0; i < nNodeArray.length; i++) {
3120
- SN_StripRemoveNode(nNodeArray[i]);
3121
- }
3122
- }
3123
-
3124
- if (!isRemoveFormat && pNode.childNodes.length === 0) {
3125
- if (element.childNodes) {
3126
- container = element.childNodes[0];
3127
- } else {
3128
- container = dom.utils.createTextNode(unicode.zeroWidthSpace);
3129
- element.appendChild(container);
3130
- }
3131
- } else {
3132
- if (!isRemoveNode && newInnerNode.textContent.length === 0) {
3133
- this.nodeTransform.removeEmptyNode(pNode, null, false);
3134
- return {
3135
- ancestor: null,
3136
- container: null,
3137
- offset: 0
3138
- };
3139
- }
3140
-
3141
- this.nodeTransform.removeEmptyNode(pNode, newInnerNode, false);
3142
-
3143
- if (dom.check.isZeroWidth(pNode.textContent)) {
3144
- container = pNode.firstChild;
3145
- offset = container.textContent.length;
3146
- } else if (dom.check.isZeroWidth(container)) {
3147
- container = newInnerNode;
3148
- offset = 1;
3149
- }
3150
-
3151
- // node change
3152
- const offsets = {
3153
- s: 0,
3154
- e: 0
3155
- };
3156
- const path = dom.query.getNodePath(container, pNode, offsets);
3157
- offset += offsets.s;
3158
-
3159
- // tag merge
3160
- const newOffsets = this.nodeTransform.mergeSameTags(pNode, [path], true);
3161
-
3162
- element.parentNode.replaceChild(pNode, element);
3163
-
3164
- container = dom.query.getNodeFromPath(path, pNode);
3165
- offset += newOffsets[0];
3166
- }
3167
-
3168
- return {
3169
- ancestor: pNode,
3170
- container: container,
3171
- offset: container.nodeType === 1 && offset === 1 ? container.childNodes.length : offset
3172
- };
3173
- },
3174
-
3175
- /**
3176
- * @private
3177
- * @this {FormatThis}
3178
- * @description Node with font-size style
3179
- * @param {Node} element Element to check
3180
- * @returns {boolean}
3181
- */
3182
- _sn_isSizeNode(element) {
3183
- return element && typeof element !== 'string' && element.nodeType !== 3 && this.isTextStyleNode(element) && !!element.style.fontSize;
3184
- },
3185
-
3186
- /**
3187
- * @private
3188
- * @this {FormatThis}
3189
- * @description Return the parent maintained tag. (bind and use a util object)
3190
- * @param {boolean} _isRemove is remove anchor
3191
- * @param {boolean} _isSizeNode is size span node
3192
- * @param {Node} element Element
3193
- * @returns {Node|null}
3194
- */
3195
- _sn_getMaintainedNode(_isRemove, _isSizeNode, element) {
3196
- if (!element || _isRemove) return null;
3197
- return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) || (!_isSizeNode ? dom.query.getParentElement(element, this._sn_isSizeNode.bind(this)) : null);
3198
- },
3199
-
3200
- /**
3201
- * @private
3202
- * @this {FormatThis}
3203
- * @description Check if element is a tag that should be persisted. (bind and use a util object)
3204
- * @param {boolean} _isRemove is remove anchor
3205
- * @param {boolean} _isSizeNode is size span node
3206
- * @param {Node} element Element
3207
- * @returns {boolean}
3208
- */
3209
- _sn_isMaintainedNode(_isRemove, _isSizeNode, element) {
3210
- if (!element || _isRemove || element.nodeType !== 1) return false;
3211
- const anchor = this._isNonSplitNode(element);
3212
- return dom.query.getParentElement(element, this._isNonSplitNode.bind(this)) ? anchor : anchor || (!_isSizeNode ? this._sn_isSizeNode(element) : false);
3213
- },
3214
-
3215
- /**
3216
- * @private
3217
- * @this {FormatThis}
3218
- * @description If certain styles are applied to all child nodes of the list cell, the style of the list cell is also changed. (bold, color, size)
3219
- * @param {Node} el List cell element. <li>
3220
- * @param {?Node} child Variable for recursive call. ("null" on the first call)
3221
- */
3222
- _sn_setCommonListStyle(el, child) {
3223
- if (!dom.check.isListCell(el)) return;
3224
-
3225
- const children = dom.utils.arrayFilter((child || el).childNodes, (current) => !dom.check.isBreak(current));
3226
- child = children[0];
3227
-
3228
- if (!dom.check.isElement(child) || children.length > 1) return;
3229
-
3230
- // set cell style---
3231
- const childStyle = child.style;
3232
- const elStyle = el.style;
3233
- const nodeName = child.nodeName.toLowerCase();
3234
- let appliedEl = false;
3235
-
3236
- // bold, italic
3237
- if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').bold.toLowerCase()) elStyle.fontWeight = 'bold';
3238
- if (this.options.get('_defaultStyleTagMap')[nodeName] === this.options.get('_defaultTagCommand').italic.toLowerCase()) elStyle.fontStyle = 'italic';
3239
-
3240
- // styles
3241
- const cKeys = converter.getValues(childStyle);
3242
- if (cKeys.length > 0) {
3243
- for (let i = 0, len = this._listCamel.length; i < len; i++) {
3244
- if (cKeys.includes(this._listKebab[i])) {
3245
- elStyle[this._listCamel[i]] = childStyle[this._listCamel[i]];
3246
- childStyle.removeProperty(this._listKebab[i]);
3247
- appliedEl = true;
3248
- }
3249
- }
3250
- }
3251
-
3252
- this._sn_setCommonListStyle(el, child);
3253
- if (!appliedEl) return;
3254
-
3255
- // common style
3256
- if (!childStyle.length) {
3257
- const ch = child.childNodes;
3258
- const p = child.parentNode;
3259
- const n = child.nextSibling;
3260
- while (ch.length > 0) {
3261
- p.insertBefore(ch[0], n);
3262
- }
3263
- dom.utils.removeItem(child);
3264
- }
3265
- },
3266
-
3267
- /**
3268
- * @private
3269
- * @this {FormatThis}
3270
- * @description Watch the applied text nodes and adjust the common styles of the list.
3271
- * @param {Node} el "LI" element
3272
- * @param {Array|null} styleArray Refer style array
3273
- */
3274
- _sn_resetCommonListCell(el, styleArray) {
3275
- if (!dom.check.isListCell(el)) return;
3276
- if (!styleArray) styleArray = this._listKebab;
3277
-
3278
- const children = dom.utils.arrayFilter(el.childNodes, (current) => !dom.check.isBreak(current));
3279
- const elStyles = el.style;
3280
-
3281
- const ec = [],
3282
- ek = [],
3283
- elKeys = converter.getValues(elStyles);
3284
- for (let i = 0, len = this._listKebab.length; i < len; i++) {
3285
- if (elKeys.includes(this._listKebab[i]) && styleArray.includes(this._listKebab[i])) {
3286
- ec.push(this._listCamel[i]);
3287
- ek.push(this._listKebab[i]);
3288
- }
3289
- }
3290
-
3291
- if (!ec.length) return;
3292
-
3293
- // reset cell style---
3294
- const refer = dom.utils.createElement('SPAN');
3295
- for (let i = 0, len = ec.length; i < len; i++) {
3296
- refer.style[ec[i]] = elStyles[ek[i]];
3297
- elStyles.removeProperty(ek[i]);
3298
- }
3299
-
3300
- let sel = refer.cloneNode(false);
3301
- let r = null,
3302
- appliedEl = false;
3303
- for (let i = 0, len = children.length, c, s; i < len; i++) {
3304
- c = /** @type {HTMLElement} */ (children[i]);
3305
- if (this.options.get('_defaultStyleTagMap')[c.nodeName.toLowerCase()]) continue;
3306
-
3307
- s = converter.getValues(c.style);
3308
- if (
3309
- s.length === 0 ||
3310
- (ec.some(function (k) {
3311
- return !s.includes(k);
3312
- }) &&
3313
- s.some(function (k) {
3314
- ec.includes(k);
3315
- }))
3316
- ) {
3317
- r = c.nextSibling;
3318
- sel.appendChild(c);
3319
- } else if (sel.childNodes.length > 0) {
3320
- el.insertBefore(sel, r);
3321
- sel = refer.cloneNode(false);
3322
- r = null;
3323
- appliedEl = true;
3324
- }
3325
- }
3326
-
3327
- if (sel.childNodes.length > 0) {
3328
- el.insertBefore(sel, r);
3329
- appliedEl = true;
3330
- }
3331
- if (!elStyles.length) {
3332
- el.removeAttribute('style');
3333
- }
3334
-
3335
- return appliedEl;
3336
- },
3337
-
3338
- /**
3339
- * @private
3340
- * @this {FormatThis}
3341
- * @description Reset the line break format.
3342
- * @param {"line"|"br"} breakFormat options.get('defaultLineBreakFormat')
3343
- */
3344
- __resetBrLineBreak(breakFormat) {
3345
- this._brLineBreak = breakFormat === 'br';
3346
- },
3347
-
3348
- constructor: Format
3349
- };
3350
-
3351
- /**
3352
- * @private
3353
- * @param {Node} baseNode Node
3354
- */
3355
- function DeleteNestedList(baseNode) {
3356
- const baseParent = baseNode.parentNode;
3357
- let parent = baseParent.parentNode;
3358
- let siblingNode = /** @type {*} */ (baseParent);
3359
- let liSibling, liParent, child, index, c;
3360
-
3361
- while (dom.check.isListCell(parent)) {
3362
- index = dom.query.getPositionIndex(baseNode);
3363
- liSibling = parent.nextElementSibling;
3364
- liParent = parent.parentNode;
3365
- child = siblingNode;
3366
-
3367
- while (child) {
3368
- siblingNode = siblingNode.nextSibling;
3369
- if (dom.check.isList(child)) {
3370
- c = child.childNodes;
3371
- while (c[index]) {
3372
- liParent.insertBefore(c[index], liSibling);
3373
- }
3374
- if (c.length === 0) dom.utils.removeItem(child);
3375
- } else {
3376
- liParent.appendChild(child);
3377
- }
3378
- child = siblingNode;
3379
- }
3380
-
3381
- parent = liParent.parentNode;
3382
- }
3383
-
3384
- if (baseParent.children.length === 0) dom.utils.removeItem(baseParent);
3385
-
3386
- return liParent;
3387
- }
3388
-
3389
- /**
3390
- * @private
3391
- * @param {Array<HTMLElement>} lines - Line elements
3392
- * @param {number} size - Margin size
3393
- * @param {string} dir - Direction
3394
- * @returns
3395
- */
3396
- function SetLineMargin(lines, size, dir) {
3397
- const cells = [];
3398
-
3399
- for (let i = 0, len = lines.length, f, margin; i < len; i++) {
3400
- f = lines[i];
3401
- if (!dom.check.isListCell(f)) {
3402
- margin = /\d+/.test(f.style[dir]) ? numbers.get(f.style[dir], 0) : 0;
3403
- margin += size;
3404
- dom.utils.setStyle(f, dir, margin <= 0 ? '' : margin + 'px');
3405
- } else {
3406
- if (size < 0 || f.previousElementSibling) {
3407
- cells.push(f);
3408
- }
3409
- }
3410
- }
3411
-
3412
- return cells;
3413
- }
3414
-
3415
- /**
3416
- * @private
3417
- * @description Strip remove node
3418
- * @param {Node} removeNode The remove node
3419
- * @private
3420
- */
3421
- function SN_StripRemoveNode(removeNode) {
3422
- const element = removeNode.parentNode;
3423
- if (!removeNode || removeNode.nodeType === 3 || !element) return;
3424
-
3425
- const children = removeNode.childNodes;
3426
- while (children[0]) {
3427
- element.insertBefore(children[0], removeNode);
3428
- }
3429
-
3430
- element.removeChild(removeNode);
3431
- }
3432
-
3433
- export default Format;