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,1376 +0,0 @@
1
- import EditorInjector from '../../editorInjector';
2
- import { Modal, Figure, FileManager, ModalAnchorEditor } from '../../modules';
3
- import { dom, numbers, env, keyCodeMap } from '../../helper';
4
- const { NO_EVENT } = env;
5
-
6
- /**
7
- * @typedef {import('../../events').ImageInfo} ImageInfo_image
8
- */
9
-
10
- /**
11
- * @typedef {import('../../modules/Figure').FigureControls} FigureControls_image
12
- */
13
-
14
- /**
15
- * @typedef {Object} ImagePluginOptions
16
- * @property {boolean} [canResize=true] - Whether the image element can be resized.
17
- * @property {boolean} [showHeightInput=true] - Whether to display the height input field.
18
- * @property {string} [defaultWidth="auto"] - The default width of the image. If a number is provided, "px" will be appended.
19
- * @property {string} [defaultHeight="auto"] - The default height of the image. If a number is provided, "px" will be appended.
20
- * @property {boolean} [percentageOnlySize=false] - Whether to allow only percentage-based sizing.
21
- * @property {boolean} [createFileInput=true] - Whether to create a file input element for image uploads.
22
- * @property {boolean} [createUrlInput=true] - Whether to create a URL input element for image insertion.
23
- * @property {string} [uploadUrl] - The URL endpoint for image file uploads.
24
- * @property {Object<string, string>} [uploadHeaders] - Additional headers to include in the file upload request.
25
- * @property {number} [uploadSizeLimit] - The total upload size limit in bytes.
26
- * @property {number} [uploadSingleSizeLimit] - The single file upload size limit in bytes.
27
- * @property {boolean} [allowMultiple=false] - Whether multiple image uploads are allowed.
28
- * @property {string} [acceptedFormats="image/*"] - The accepted file formats for image uploads.
29
- * @property {boolean} [useFormatType=true] - Whether to enable format type selection (block or inline).
30
- * @property {string} [defaultFormatType="block"] - The default image format type ("block" or "inline").
31
- * @property {boolean} [keepFormatType=false] - Whether to retain the chosen format type after image insertion.
32
- * @property {boolean} [linkEnableFileUpload] - Whether to enable file uploads for linked images.
33
- * @property {FigureControls_image} [controls] - Figure controls.
34
- */
35
-
36
- /**
37
- * @class
38
- * @description Image plugin.
39
- * - This plugin provides image insertion functionality within the editor, supporting both file upload and URL input.
40
- */
41
- class Image_ extends EditorInjector {
42
- static key = 'image';
43
- static type = 'modal';
44
- static className = '';
45
- /**
46
- * @this {Image_}
47
- * @param {Element} node - The node to check.
48
- * @returns {Element|null} Returns a node if the node is a valid component.
49
- */
50
- static component(node) {
51
- const compNode = dom.check.isFigure(node) || (/^span$/i.test(node.nodeName) && dom.utils.hasClass(node, 'se-component')) ? node.firstElementChild : node;
52
- return /^IMG$/i.test(compNode?.nodeName) ? compNode : dom.check.isAnchor(compNode) && /^IMG$/i.test(compNode?.firstElementChild?.nodeName) ? compNode?.firstElementChild : null;
53
- }
54
-
55
- /**
56
- * @constructor
57
- * @param {__se__EditorCore} editor - The root editor instance
58
- * @param {ImagePluginOptions} pluginOptions
59
- */
60
- constructor(editor, pluginOptions) {
61
- // plugin bisic properties
62
- super(editor);
63
- this.title = this.lang.image;
64
- this.icon = 'image';
65
-
66
- this.pluginOptions = {
67
- canResize: pluginOptions.canResize === undefined ? true : pluginOptions.canResize,
68
- showHeightInput: pluginOptions.showHeightInput === undefined ? true : !!pluginOptions.showHeightInput,
69
- defaultWidth: !pluginOptions.defaultWidth ? 'auto' : numbers.is(pluginOptions.defaultWidth) ? pluginOptions.defaultWidth + 'px' : pluginOptions.defaultWidth,
70
- defaultHeight: !pluginOptions.defaultHeight ? 'auto' : numbers.is(pluginOptions.defaultHeight) ? pluginOptions.defaultHeight + 'px' : pluginOptions.defaultHeight,
71
- percentageOnlySize: !!pluginOptions.percentageOnlySize,
72
- createFileInput: pluginOptions.createFileInput === undefined ? true : pluginOptions.createFileInput,
73
- createUrlInput: pluginOptions.createUrlInput === undefined || !pluginOptions.createFileInput ? true : pluginOptions.createUrlInput,
74
- uploadUrl: typeof pluginOptions.uploadUrl === 'string' ? pluginOptions.uploadUrl : null,
75
- uploadHeaders: pluginOptions.uploadHeaders || null,
76
- uploadSizeLimit: numbers.get(pluginOptions.uploadSizeLimit, 0),
77
- uploadSingleSizeLimit: numbers.get(pluginOptions.uploadSingleSizeLimit, 0),
78
- allowMultiple: !!pluginOptions.allowMultiple,
79
- acceptedFormats: typeof pluginOptions.acceptedFormats !== 'string' || pluginOptions.acceptedFormats.trim() === '*' ? 'image/*' : pluginOptions.acceptedFormats.trim() || 'image/*',
80
- useFormatType: pluginOptions.useFormatType ?? true,
81
- defaultFormatType: ['block', 'inline'].includes(pluginOptions.defaultFormatType) ? pluginOptions.defaultFormatType : 'block',
82
- keepFormatType: pluginOptions.keepFormatType ?? false
83
- };
84
-
85
- // create HTML
86
- const sizeUnit = this.pluginOptions.percentageOnlySize ? '%' : 'px';
87
- const modalEl = CreateHTML_modal(editor, this.pluginOptions);
88
- const ctrlAs = this.pluginOptions.useFormatType ? 'as' : '';
89
- const figureControls =
90
- pluginOptions.controls || !this.pluginOptions.canResize
91
- ? [[ctrlAs, 'mirror_h', 'mirror_v', 'align', 'caption', 'edit', 'revert', 'copy', 'remove']]
92
- : [
93
- [ctrlAs, 'resize_auto,100,75,50', 'rotate_l', 'rotate_r', 'mirror_h', 'mirror_v'],
94
- ['align', 'caption', 'edit', 'revert', 'copy', 'remove']
95
- ];
96
-
97
- // show align
98
- this.alignForm = modalEl.alignForm;
99
- if (!figureControls.some((subArray) => subArray.includes('align'))) this.alignForm.style.display = 'none';
100
-
101
- // modules
102
- const Link = this.plugins.link ? this.plugins.link.pluginOptions : {};
103
- this.anchor = new ModalAnchorEditor(this, modalEl.html, {
104
- textToDisplay: false,
105
- title: true,
106
- openNewWindow: Link.openNewWindow,
107
- relList: Link.relList,
108
- defaultRel: Link.defaultRel,
109
- noAutoPrefix: Link.noAutoPrefix,
110
- enableFileUpload: pluginOptions.linkEnableFileUpload
111
- });
112
- this.modal = new Modal(this, modalEl.html);
113
- this.figure = new Figure(this, figureControls, {
114
- sizeUnit: sizeUnit
115
- });
116
- this.fileManager = new FileManager(this, {
117
- query: 'img',
118
- loadHandler: this.events.onImageLoad,
119
- eventHandler: this.events.onImageAction
120
- });
121
-
122
- // members
123
- this.fileModalWrapper = modalEl.fileModalWrapper;
124
- this.imgInputFile = modalEl.imgInputFile;
125
- this.imgUrlFile = modalEl.imgUrlFile;
126
- this.focusElement = this.imgInputFile || this.imgUrlFile;
127
- this.altText = modalEl.altText;
128
- this.captionCheckEl = modalEl.captionCheckEl;
129
- this.captionEl = this.captionCheckEl?.parentElement;
130
- this.previewSrc = modalEl.previewSrc;
131
- this.sizeUnit = sizeUnit;
132
- this.as = 'block';
133
- this.proportion = null;
134
- this.inputX = null;
135
- this.inputY = null;
136
- this._linkElement = null;
137
- this._linkValue = '';
138
- this._align = 'none';
139
- this._svgDefaultSize = '30%';
140
- this._base64RenderIndex = 0;
141
- this._element = null;
142
- this._cover = null;
143
- this._container = null;
144
- this._caption = null;
145
- this._ratio = {
146
- w: 1,
147
- h: 1
148
- };
149
- this._origin_w = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
150
- this._origin_h = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
151
- this._resizing = this.pluginOptions.canResize;
152
- this._onlyPercentage = this.pluginOptions.percentageOnlySize;
153
- this._nonResizing = !this._resizing || !this.pluginOptions.showHeightInput || this._onlyPercentage;
154
-
155
- // init
156
- this.eventManager.addEvent(modalEl.tabs, 'click', this.#OpenTab.bind(this));
157
- if (this.imgInputFile) this.eventManager.addEvent(modalEl.fileRemoveBtn, 'click', this.#RemoveSelectedFiles.bind(this));
158
- if (this.imgUrlFile) this.eventManager.addEvent(this.imgUrlFile, 'input', this.#OnLinkPreview.bind(this));
159
- if (this.imgInputFile && this.imgUrlFile) this.eventManager.addEvent(this.imgInputFile, 'change', this.#OnfileInputChange.bind(this));
160
-
161
- const galleryButton = modalEl.galleryButton;
162
- if (galleryButton) this.eventManager.addEvent(galleryButton, 'click', this.#OpenGallery.bind(this));
163
-
164
- if (this._resizing) {
165
- this.proportion = modalEl.proportion;
166
- this.inputX = modalEl.inputX;
167
- this.inputY = modalEl.inputY;
168
- this.inputX.value = this.pluginOptions.defaultWidth;
169
- this.inputY.value = this.pluginOptions.defaultHeight;
170
-
171
- const ratioChange = this.#OnChangeRatio.bind(this);
172
- this.eventManager.addEvent(this.inputX, 'keyup', this.#OnInputSize.bind(this, 'x'));
173
- this.eventManager.addEvent(this.inputY, 'keyup', this.#OnInputSize.bind(this, 'y'));
174
- this.eventManager.addEvent(this.inputX, 'change', ratioChange);
175
- this.eventManager.addEvent(this.inputY, 'change', ratioChange);
176
- this.eventManager.addEvent(this.proportion, 'change', ratioChange);
177
- this.eventManager.addEvent(modalEl.revertBtn, 'click', this.#OnClickRevert.bind(this));
178
- }
179
-
180
- if (this.pluginOptions.useFormatType) {
181
- this.as = this.pluginOptions.defaultFormatType;
182
- this.asBlock = modalEl.asBlock;
183
- this.asInline = modalEl.asInline;
184
- this.eventManager.addEvent([this.asBlock, this.asInline], 'click', this.#OnClickAsButton.bind(this));
185
- }
186
- }
187
-
188
- /**
189
- * @editorMethod Modules.Modal
190
- * @description Executes the method that is called when a "Modal" module's is opened.
191
- */
192
- open() {
193
- this.modal.open();
194
- }
195
-
196
- /**
197
- * @editorMethod Modules.Controller(Figure)
198
- * @description Executes the method that is called when a target component is edited.
199
- */
200
- edit() {
201
- this.modal.open();
202
- }
203
-
204
- /**
205
- * @editorMethod Modules.Modal
206
- * @description Executes the method that is called when a plugin's modal is opened.
207
- * @param {boolean} isUpdate "Indicates whether the modal is for editing an existing component (true) or registering a new one (false)."
208
- */
209
- on(isUpdate) {
210
- if (!isUpdate) {
211
- if (this._resizing) {
212
- this.inputX.value = this._origin_w = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
213
- this.inputY.value = this._origin_h = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
214
- }
215
- if (this.imgInputFile && this.pluginOptions.allowMultiple) this.imgInputFile.setAttribute('multiple', 'multiple');
216
- } else {
217
- if (this.imgInputFile && this.pluginOptions.allowMultiple) this.imgInputFile.removeAttribute('multiple');
218
- }
219
-
220
- this.anchor.on(isUpdate);
221
- }
222
-
223
- /**
224
- * @editorMethod Editor.EventManager
225
- * @description Executes the event function of "paste" or "drop".
226
- * @param {Object} params { frameContext, event, file }
227
- * @param {__se__FrameContext} params.frameContext Frame context
228
- * @param {ClipboardEvent} params.event Event object
229
- * @param {File} params.file File object
230
- * @returns {boolean} - If return false, the file upload will be canceled
231
- */
232
- onFilePasteAndDrop({ file }) {
233
- if (!/^image/.test(file.type)) return;
234
-
235
- this.submitFile([file]);
236
- this.editor.focus();
237
-
238
- return false;
239
- }
240
-
241
- /**
242
- * @editorMethod Modules.Modal
243
- * @description This function is called when a form within a modal window is "submit".
244
- * @returns {Promise<boolean>} Success or failure
245
- */
246
- async modalAction() {
247
- this._align = /** @type {HTMLInputElement} */ (this.modal.form.querySelector('input[name="suneditor_image_radio"]:checked')).value;
248
-
249
- if (this.modal.isUpdate) {
250
- this._update(this.inputX?.value, this.inputY?.value);
251
- this.history.push(false);
252
- }
253
-
254
- if (this.imgInputFile && this.imgInputFile.files.length > 0) {
255
- return await this.submitFile(this.imgInputFile.files);
256
- } else if (this.imgUrlFile && this._linkValue.length > 0) {
257
- return await this.submitURL(this._linkValue);
258
- }
259
-
260
- return false;
261
- }
262
-
263
- /**
264
- * @editorMethod Editor.core
265
- * @description This method is used to validate and preserve the format of the component within the editor.
266
- * - It ensures that the structure and attributes of the element are maintained and secure.
267
- * - The method checks if the element is already wrapped in a valid container and updates its attributes if necessary.
268
- * - If the element isn't properly contained, a new container is created to retain the format.
269
- * @returns {{query: string, method: (element: HTMLImageElement) => void}} The format retention object containing the query and method to process the element.
270
- * - query: The selector query to identify the relevant elements (in this case, 'audio').
271
- * - method:The function to execute on the element to validate and preserve its format.
272
- * - The function takes the element as an argument, checks if it is contained correctly, and applies necessary adjustments.
273
- */
274
- retainFormat() {
275
- return {
276
- query: 'img',
277
- method: (element) => {
278
- const figureInfo = Figure.GetContainer(element);
279
- if (figureInfo && figureInfo.container && figureInfo.cover) return;
280
-
281
- this._ready(element);
282
- this._fileCheck(this._origin_w, this._origin_h);
283
- }
284
- };
285
- }
286
-
287
- /**
288
- * @editorMethod Modules.Modal
289
- * @description This function is called before the modal window is opened, but before it is closed.
290
- */
291
- init() {
292
- Modal.OnChangeFile(this.fileModalWrapper, []);
293
- if (this.imgInputFile) this.imgInputFile.value = '';
294
- if (this.imgUrlFile) this._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = '';
295
- if (this.imgInputFile && this.imgUrlFile) {
296
- this.imgUrlFile.disabled = false;
297
- this.previewSrc.style.textDecoration = '';
298
- }
299
-
300
- this.altText.value = '';
301
- /** @type {HTMLInputElement} */ (this.modal.form.querySelector('input[name="suneditor_image_radio"][value="none"]')).checked = true;
302
- this.captionCheckEl.checked = false;
303
- this._element = null;
304
- this._ratio = {
305
- w: 1,
306
- h: 1
307
- };
308
- this.#OpenTab('init');
309
-
310
- if (this._resizing) {
311
- this.inputX.value = this.pluginOptions.defaultWidth === 'auto' ? '' : this.pluginOptions.defaultWidth;
312
- this.inputY.value = this.pluginOptions.defaultHeight === 'auto' ? '' : this.pluginOptions.defaultHeight;
313
- this.proportion.checked = true;
314
- }
315
-
316
- if (this.pluginOptions.useFormatType) {
317
- this._activeAsInline((this.pluginOptions.keepFormatType ? this.as : this.pluginOptions.defaultFormatType) === 'inline');
318
- }
319
-
320
- this.anchor.init();
321
- }
322
-
323
- /**
324
- * @editorMethod Editor.Component
325
- * @description Executes the method that is called when a component of a plugin is selected.
326
- * @param {HTMLElement} target Target component element
327
- */
328
- select(target) {
329
- this._ready(target);
330
- }
331
-
332
- /**
333
- * @private
334
- * @description Prepares the component for selection.
335
- * - Ensures that the controller is properly positioned and initialized.
336
- * - Prevents duplicate event handling if the component is already selected.
337
- * @param {HTMLElement} target - The selected element.
338
- */
339
- _ready(target) {
340
- if (!target) return;
341
- const figureInfo = this.figure.open(target, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: false });
342
- this.anchor.set(dom.check.isAnchor(target.parentNode) ? target.parentNode : null);
343
-
344
- this._linkElement = this.anchor.currentTarget;
345
- this._element = target;
346
- this._cover = figureInfo.cover;
347
- this._container = figureInfo.container;
348
- this._caption = figureInfo.caption;
349
- this._align = figureInfo.align;
350
- target.style.float = '';
351
-
352
- this._origin_w = String(figureInfo.originWidth || figureInfo.w || '');
353
- this._origin_h = String(figureInfo.originHeight || figureInfo.h || '');
354
- this.altText.value = this._element.alt;
355
-
356
- if (this.imgUrlFile) this._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = this._element.src;
357
-
358
- /** @type {HTMLInputElement} */
359
- const activeAlign = this.modal.form.querySelector('input[name="suneditor_image_radio"][value="' + this._align + '"]') || this.modal.form.querySelector('input[name="suneditor_image_radio"][value="none"]');
360
- activeAlign.checked = true;
361
- this.captionCheckEl.checked = !!this._caption;
362
-
363
- if (!this._resizing) return;
364
-
365
- const percentageRotation = this._onlyPercentage && this.figure.isVertical;
366
- let w = percentageRotation ? '' : figureInfo.width;
367
- if (this._onlyPercentage) {
368
- w = numbers.get(w, 2);
369
- if (w > 100) w = 100;
370
- }
371
- this.inputX.value = String(w === 'auto' ? '' : w);
372
-
373
- if (!this._onlyPercentage) {
374
- const h = percentageRotation ? '' : figureInfo.height;
375
- this.inputY.value = String(h === 'auto' ? '' : h);
376
- }
377
-
378
- this.proportion.checked = true;
379
- this.inputX.disabled = percentageRotation ? true : false;
380
- this.inputY.disabled = percentageRotation ? true : false;
381
- this.proportion.disabled = percentageRotation ? true : false;
382
-
383
- this._ratio = this.proportion.checked
384
- ? figureInfo.ratio
385
- : {
386
- w: 1,
387
- h: 1
388
- };
389
-
390
- if (this.pluginOptions.useFormatType) {
391
- this._activeAsInline(this.component.isInline(figureInfo.container));
392
- }
393
- }
394
-
395
- /**
396
- * @editorMethod Editor.Component
397
- * @description Method to delete a component of a plugin, called by the "FileManager", "Controller" module.
398
- * @param {HTMLElement} target Target element
399
- * @returns {Promise<void>}
400
- */
401
- async destroy(target) {
402
- const targetEl = target || this._element;
403
- const container = dom.query.getParentElement(targetEl, Figure.is) || targetEl;
404
- const focusEl = container.previousElementSibling || container.nextElementSibling;
405
- const emptyDiv = container.parentNode;
406
-
407
- const message = await this.triggerEvent('onImageDeleteBefore', { element: targetEl, container, align: this._align, alt: this.altText.value, url: this._linkValue });
408
- if (message === false) return;
409
-
410
- dom.utils.removeItem(container);
411
- this.init();
412
-
413
- if (emptyDiv !== this.editor.frameContext.get('wysiwyg')) {
414
- this.nodeTransform.removeAllParents(
415
- emptyDiv,
416
- function (current) {
417
- return current.childNodes.length === 0;
418
- },
419
- null
420
- );
421
- }
422
-
423
- // focus
424
- this.editor.focusEdge(focusEl);
425
- this.history.push(false);
426
- }
427
-
428
- /**
429
- * @private
430
- * @description Retrieves the current image information.
431
- * @returns {*} - The image data.
432
- */
433
- _getInfo() {
434
- return {
435
- element: this._element,
436
- anchor: this.anchor.create(true),
437
- inputWidth: this.inputX?.value || '',
438
- inputHeight: this.inputY?.value || '',
439
- align: this._align,
440
- isUpdate: this.modal.isUpdate,
441
- alt: this.altText.value
442
- };
443
- }
444
-
445
- /**
446
- * @private
447
- * @description Toggles between block and inline image format.
448
- * @param {boolean} isInline - Whether the image should be inline.
449
- */
450
- _activeAsInline(isInline) {
451
- if (isInline) {
452
- dom.utils.addClass(this.asInline, 'on');
453
- dom.utils.removeClass(this.asBlock, 'on');
454
- this.as = 'inline';
455
- // buttns
456
- if (this.alignForm) this.alignForm.style.display = 'none';
457
- // caption
458
- if (this.captionEl) this.captionEl.style.display = 'none';
459
- } else {
460
- dom.utils.addClass(this.asBlock, 'on');
461
- dom.utils.removeClass(this.asInline, 'on');
462
- this.as = 'block';
463
- // buttns
464
- if (this.alignForm) this.alignForm.style.display = '';
465
- // caption
466
- if (this.captionEl) this.captionEl.style.display = '';
467
- }
468
- }
469
-
470
- /**
471
- * @description Create an "image" component using the provided files.
472
- * @param {FileList|File[]} fileList File object list
473
- * @returns {Promise<boolean>} If return false, the file upload will be canceled
474
- */
475
- async submitFile(fileList) {
476
- if (fileList.length === 0) return false;
477
-
478
- let fileSize = 0;
479
- const files = [];
480
- const slngleSizeLimit = this.pluginOptions.uploadSingleSizeLimit;
481
- for (let i = 0, len = fileList.length, f, s; i < len; i++) {
482
- f = fileList[i];
483
- if (!/image/i.test(f.type)) continue;
484
-
485
- s = f.size;
486
- if (slngleSizeLimit && slngleSizeLimit > s) {
487
- const err = '[SUNEDITOR.imageUpload.fail] Size of uploadable single file: ' + slngleSizeLimit / 1000 + 'KB';
488
- const message = await this.triggerEvent('onImageUploadError', {
489
- error: err,
490
- limitSize: slngleSizeLimit,
491
- uploadSize: s,
492
- file: f
493
- });
494
-
495
- this.ui.alertOpen(message === NO_EVENT ? err : message || err, 'error');
496
-
497
- return false;
498
- }
499
-
500
- files.push(f);
501
- fileSize += s;
502
- }
503
-
504
- const limitSize = this.pluginOptions.uploadSizeLimit;
505
- const currentSize = this.fileManager.getSize();
506
- if (limitSize > 0 && fileSize + currentSize > limitSize) {
507
- const err = '[SUNEDITOR.imageUpload.fail] Size of uploadable total images: ' + limitSize / 1000 + 'KB';
508
- const message = await this.triggerEvent('onImageUploadError', {
509
- error: err,
510
- limitSize,
511
- currentSize,
512
- uploadSize: fileSize
513
- });
514
-
515
- this.ui.alertOpen(message === NO_EVENT ? err : message || err, 'error');
516
-
517
- return false;
518
- }
519
-
520
- const imgInfo = { files, ...this._getInfo() };
521
- const handler = function (infos, newInfos) {
522
- infos = newInfos || infos;
523
- this._serverUpload(infos, infos.files);
524
- }.bind(this, imgInfo);
525
- // se-ts-ignore
526
- this._serverUpload;
527
-
528
- const result = await this.triggerEvent('onImageUploadBefore', {
529
- info: imgInfo,
530
- handler
531
- });
532
-
533
- if (result === undefined) return true;
534
- if (result === false) return false;
535
- if (result !== null && typeof result === 'object') handler(result);
536
-
537
- if (result === true || result === NO_EVENT) handler(null);
538
- }
539
-
540
- /**
541
- * @description Create an "image" component using the provided url.
542
- * @param {string} url File url
543
- * @returns {Promise<boolean>} If return false, the file upload will be canceled
544
- */
545
- async submitURL(url) {
546
- if (!url) url = this._linkValue;
547
- if (!url) return false;
548
-
549
- const file = { name: url.split('/').pop(), size: 0 };
550
- const imgInfo = {
551
- url,
552
- files: file,
553
- ...this._getInfo()
554
- };
555
-
556
- const handler = function (infos, newInfos) {
557
- infos = newInfos || infos;
558
- const infoUrl = infos.url;
559
- if (this.modal.isUpdate) this._updateSrc(infoUrl, infos.element, infos.files);
560
- else this._produce(infoUrl, infos.anchor, infos.inputWidth, infos.inputHeight, infos.align, infos.files, infos.alt);
561
- }.bind(this, imgInfo);
562
-
563
- const result = await this.triggerEvent('onImageUploadBefore', {
564
- info: imgInfo,
565
- handler
566
- });
567
-
568
- if (result === undefined) return true;
569
- if (result === false) return false;
570
- if (result !== null && typeof result === 'object') handler(result);
571
-
572
- if (result === true || result === NO_EVENT) handler(null);
573
-
574
- return true;
575
- }
576
-
577
- /**
578
- * @private
579
- * @description Updates the selected image size, alt text, and caption.
580
- * @param {string} width - New image width.
581
- * @param {string} height - New image height.
582
- */
583
- _update(width, height) {
584
- if (!width) width = this.inputX?.value || 'auto';
585
- if (!height) height = this.inputY?.value || 'auto';
586
-
587
- let imageEl = this._element;
588
- const cover = this._cover;
589
- const container = this._container === this._cover ? null : this._container;
590
-
591
- // check size
592
- let changeSize;
593
- const x = numbers.is(width) ? width + this.sizeUnit : width;
594
- const y = numbers.is(height) ? height + this.sizeUnit : height;
595
- if (/%$/.test(imageEl.style.width)) {
596
- changeSize = x !== container.style.width || y !== container.style.height;
597
- } else {
598
- changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
599
- }
600
-
601
- // alt
602
- imageEl.alt = this.altText.value;
603
-
604
- // caption
605
- let modifiedCaption = false;
606
- if (this.captionCheckEl.checked) {
607
- if (!this._caption) {
608
- this._caption = Figure.CreateCaption(cover, this.lang.caption);
609
- modifiedCaption = true;
610
- }
611
- } else {
612
- if (this._caption) {
613
- dom.utils.removeItem(this._caption);
614
- this._caption = null;
615
- modifiedCaption = true;
616
- }
617
- }
618
-
619
- // link
620
- let isNewAnchor = false;
621
- const anchor = this.anchor.create(true);
622
- if (anchor) {
623
- if (this._linkElement !== anchor || !container.contains(anchor)) {
624
- this._linkElement = anchor.cloneNode(false);
625
- cover.insertBefore(this._setAnchor(imageEl, this._linkElement), this._caption);
626
- isNewAnchor = true;
627
- }
628
- } else if (this._linkElement !== null) {
629
- if (cover.contains(this._linkElement)) {
630
- const newEl = imageEl.cloneNode(true);
631
- cover.removeChild(this._linkElement);
632
- cover.insertBefore(newEl, this._caption);
633
- imageEl = newEl;
634
- }
635
- }
636
-
637
- // size
638
- if (this._resizing && changeSize) {
639
- this._applySize(width, height);
640
- }
641
-
642
- if (isNewAnchor) {
643
- dom.utils.removeItem(anchor);
644
- }
645
-
646
- // transform
647
- if (modifiedCaption || (!this._onlyPercentage && changeSize)) {
648
- if (/\d+/.test(imageEl.style.height) || (this.figure.isVertical && this.captionCheckEl.checked)) {
649
- if (/auto|%$/.test(width) || /auto|%$/.test(height)) {
650
- this.figure.deleteTransform(imageEl);
651
- } else {
652
- this.figure.setTransform(imageEl, width, height, 0);
653
- }
654
- }
655
- }
656
-
657
- // align
658
- this.figure.setAlign(imageEl, this._align);
659
-
660
- // select
661
- imageEl.onload = () => {
662
- this.select(imageEl);
663
- };
664
- }
665
-
666
- /**
667
- * @private
668
- * @description Validates the image size and applies necessary transformations.
669
- * @param {string} width - The width of the image.
670
- * @param {string} height - The height of the image.
671
- */
672
- _fileCheck(width, height) {
673
- if (!width) width = this.inputX?.value || 'auto';
674
- if (!height) height = this.inputY?.value || 'auto';
675
-
676
- let imageEl = this._element;
677
- let cover = this._cover;
678
- let inlineCover = null;
679
- let container = this._container === this._cover ? null : this._container;
680
- let isNewContainer = false;
681
-
682
- if (!cover || !container) {
683
- isNewContainer = true;
684
- imageEl = this._element.cloneNode(true);
685
- const figureInfo =
686
- this.pluginOptions.useFormatType && width !== 'auto' && (/^span$/i.test(this._element.parentElement?.nodeName) || this.format.isLine(this._element.parentElement))
687
- ? Figure.CreateInlineContainer(imageEl, 'se-image-container')
688
- : Figure.CreateContainer(imageEl, 'se-image-container');
689
- cover = figureInfo.cover;
690
- container = figureInfo.container;
691
- inlineCover = figureInfo.inlineCover;
692
- this.figure.open(imageEl, { nonResizing: true, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
693
- }
694
-
695
- // check size
696
- let changeSize;
697
- const x = numbers.is(width) ? width + this.sizeUnit : width;
698
- const y = numbers.is(height) ? height + this.sizeUnit : height;
699
- if (/%$/.test(imageEl.style.width)) {
700
- changeSize = x !== container.style.width || y !== container.style.height;
701
- } else {
702
- changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
703
- }
704
-
705
- // alt
706
- imageEl.alt = this.altText.value;
707
-
708
- // caption
709
- let modifiedCaption = false;
710
- if (!inlineCover) {
711
- if (this.captionCheckEl.checked) {
712
- if (!this._caption || isNewContainer) {
713
- this._caption = Figure.CreateCaption(cover, this.lang.caption);
714
- modifiedCaption = true;
715
- }
716
- } else {
717
- if (this._caption) {
718
- dom.utils.removeItem(this._caption);
719
- this._caption = null;
720
- modifiedCaption = true;
721
- }
722
- }
723
- }
724
-
725
- // link
726
- let isNewAnchor = null;
727
- const anchor = this.anchor.create(true);
728
- if (anchor) {
729
- if (this._linkElement !== anchor || (isNewContainer && !container.contains(anchor))) {
730
- this._linkElement = anchor.cloneNode(false);
731
- cover.insertBefore(this._setAnchor(imageEl, this._linkElement), this._caption);
732
- isNewAnchor = this._element;
733
- }
734
- } else if (this._linkElement !== null) {
735
- if (cover.contains(this._linkElement)) {
736
- const newEl = imageEl.cloneNode(true);
737
- cover.removeChild(this._linkElement);
738
- cover.insertBefore(newEl, this._caption);
739
- imageEl = newEl;
740
- }
741
- }
742
-
743
- if (isNewContainer) {
744
- imageEl = this._element;
745
- this.figure.retainFigureFormat(container, this._element, isNewAnchor ? anchor : null, this.fileManager);
746
- this._element = imageEl = container.querySelector('img');
747
- this._cover = cover;
748
- this._container = container;
749
- }
750
-
751
- // size
752
- if (this._resizing && changeSize) {
753
- this._applySize(width, height);
754
- }
755
-
756
- if (isNewAnchor) {
757
- if (!isNewContainer) {
758
- dom.utils.removeItem(anchor);
759
- } else {
760
- dom.utils.removeItem(isNewAnchor);
761
- if (dom.query.getListChildren(anchor, (current) => /IMG/i.test(current.tagName)).length === 0) {
762
- dom.utils.removeItem(anchor);
763
- }
764
- }
765
- }
766
-
767
- // transform
768
- if (modifiedCaption || (!this._onlyPercentage && changeSize)) {
769
- if (/\d+/.test(imageEl.style.height) || (this.figure.isVertical && this.captionCheckEl.checked)) {
770
- if (/auto|%$/.test(width) || /auto|%$/.test(height)) {
771
- this.figure.deleteTransform(imageEl);
772
- } else {
773
- this.figure.setTransform(imageEl, width, height, 0);
774
- }
775
- }
776
- }
777
-
778
- // align
779
- this.figure.setAlign(imageEl, this._align);
780
- }
781
-
782
- /**
783
- * @description Opens a specific tab inside the modal.
784
- * @param {MouseEvent|string} e - The event object or tab name.
785
- * @returns {boolean} - Whether the tab was successfully opened.
786
- */
787
- #OpenTab(e) {
788
- const modalForm = this.modal.form;
789
- const targetElement = typeof e === 'string' ? modalForm.querySelector('._se_tab_link') : dom.query.getEventTarget(e);
790
-
791
- if (!/^BUTTON$/i.test(targetElement.tagName)) {
792
- return false;
793
- }
794
-
795
- // Declare all variables
796
- const tabName = targetElement.getAttribute('data-tab-link');
797
- let i;
798
-
799
- // Get all elements with class="tabcontent" and hide them
800
- const tabContent = /** @type {HTMLCollectionOf<HTMLElement>}*/ (modalForm.getElementsByClassName('_se_tab_content'));
801
- for (i = 0; i < tabContent.length; i++) {
802
- tabContent[i].style.display = 'none';
803
- }
804
-
805
- // Get all elements with class="tablinks" and remove the class "active"
806
- const tabLinks = modalForm.getElementsByClassName('_se_tab_link');
807
- for (i = 0; i < tabLinks.length; i++) {
808
- dom.utils.removeClass(tabLinks[i], 'active');
809
- }
810
-
811
- // Show the current tab, and add an "active" class to the button that opened the tab
812
- /** @type {HTMLElement}*/ (modalForm.querySelector('._se_tab_content_' + tabName)).style.display = 'block';
813
- dom.utils.addClass(targetElement, 'active');
814
-
815
- // focus
816
- if (e !== 'init') {
817
- if (tabName === 'image') {
818
- this.focusElement.focus();
819
- } else if (tabName === 'url') {
820
- this.anchor.urlInput.focus();
821
- }
822
- }
823
-
824
- return false;
825
- }
826
-
827
- /**
828
- * @private
829
- * @description Creates a new image component based on provided parameters.
830
- * @param {string} src - The image source URL.
831
- * @param {?Node} anchor - Optional anchor wrapping the image.
832
- * @param {string} width - Image width.
833
- * @param {string} height - Image height.
834
- * @param {string} align - Image alignment.
835
- * @param {{name: string, size: number}} file - File metadata.
836
- * @param {string} alt - Alternative text.
837
- */
838
- _produce(src, anchor, width, height, align, file, alt) {
839
- if (this.as !== 'inline') {
840
- this.create(src, anchor, width, height, align, file, alt);
841
- } else {
842
- this.createInline(src, anchor, width, height, file, alt);
843
- }
844
- }
845
-
846
- /**
847
- * @private
848
- * @description Applies the specified width and height to the image.
849
- * @param {string} w - Image width.
850
- * @param {string} h - Image height.
851
- */
852
- _applySize(w, h) {
853
- if (!w) w = this.inputX?.value || this.pluginOptions.defaultWidth;
854
- if (!h) h = this.inputY?.value || this.pluginOptions.defaultHeight;
855
- if (this._onlyPercentage) {
856
- if (!w) w = '100%';
857
- else if (/%$/.test(w)) w += '%';
858
- }
859
- this.figure.setSize(w, h);
860
- }
861
-
862
- /**
863
- * @description Creates a new image component, wraps it in a figure container with an optional anchor,
864
- * - applies size and alignment settings, and inserts it into the editor.
865
- * @param {string} src - The URL of the image to be inserted.
866
- * @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
867
- * @param {string} width - The width value to be applied to the image.
868
- * @param {string} height - The height value to be applied to the image.
869
- * @param {string} align - The alignment setting for the image (e.g., 'left', 'center', 'right').
870
- * @param {{name: string, size: number}} file - File metadata associated with the image
871
- * @param {string} alt - The alternative text for the image.
872
- */
873
- create(src, anchor, width, height, align, file, alt) {
874
- /** @type {HTMLImageElement} */
875
- const oImg = dom.utils.createElement('IMG');
876
- oImg.src = src;
877
- oImg.alt = alt;
878
- anchor = this._setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
879
-
880
- const figureInfo = Figure.CreateContainer(anchor, 'se-image-container');
881
- const cover = figureInfo.cover;
882
- const container = figureInfo.container;
883
-
884
- // caption
885
- if (this.captionCheckEl.checked) {
886
- this._caption = Figure.CreateCaption(cover, this.lang.caption);
887
- }
888
-
889
- this._element = oImg;
890
- this._cover = cover;
891
- this._container = container;
892
- this.figure.open(oImg, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
893
-
894
- // set size
895
- this._applySize(width, height);
896
-
897
- // align
898
- this.figure.setAlign(oImg, align);
899
-
900
- this.fileManager.setFileData(oImg, file);
901
-
902
- oImg.onload = this.#OnloadImg.bind(this, oImg, this._svgDefaultSize, container);
903
- this.component.insert(container, { skipCharCount: false, skipSelection: !this.options.get('componentAutoSelect'), skipHistory: false });
904
- }
905
-
906
- /**
907
- * @description Creates a new inline image component, wraps it in an inline figure container with an optional anchor,
908
- * - applies size settings, and inserts it into the editor.
909
- * @param {string} src - The URL of the image to be inserted.
910
- * @param {?Node} anchor - An optional anchor element to wrap the image. If provided, a clone is used.
911
- * @param {string} width - The width value to be applied to the image.
912
- * @param {string} height - The height value to be applied to the image.
913
- * @param {{name: string, size: number}} file - File metadata associated with the image
914
- * @param {string} alt - The alternative text for the image.
915
- */
916
- createInline(src, anchor, width, height, file, alt) {
917
- /** @type {HTMLImageElement} */
918
- const oImg = dom.utils.createElement('IMG');
919
- oImg.src = src;
920
- oImg.alt = alt;
921
- anchor = this._setAnchor(oImg, anchor ? anchor.cloneNode(false) : null);
922
-
923
- const figureInfo = Figure.CreateInlineContainer(anchor, 'se-image-container');
924
- const container = figureInfo.container;
925
-
926
- this._element = oImg;
927
- this._container = container;
928
- this.figure.open(oImg, { nonResizing: this._nonResizing, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
929
-
930
- // set size
931
- this._applySize(width, height);
932
-
933
- this.fileManager.setFileData(oImg, file);
934
-
935
- oImg.onload = this.#OnloadImg.bind(this, oImg, this._svgDefaultSize, container);
936
- this.component.insert(container, { skipCharCount: false, skipSelection: true, skipHistory: false });
937
- }
938
-
939
- /**
940
- * @private
941
- * @description Updates the image source URL.
942
- * @param {string} src - The new image source.
943
- * @param {HTMLImageElement} element - The image element.
944
- * @param {{ name: string, size: number }} file - File metadata.
945
- */
946
- _updateSrc(src, element, file) {
947
- element.src = src;
948
- this.fileManager.setFileData(element, file);
949
- this.component.select(element, Image_.key);
950
- }
951
-
952
- /**
953
- * @private
954
- * @description Registers the uploaded image and inserts it into the editor.
955
- * @param {ImageInfo_image} info - Image info.
956
- * @param {Object<string, *>} response - Server response data.
957
- */
958
- _register(info, response) {
959
- const fileList = response.result;
960
-
961
- for (let i = 0, len = fileList.length, file; i < len; i++) {
962
- file = {
963
- name: fileList[i].name,
964
- size: fileList[i].size
965
- };
966
- if (info.isUpdate) {
967
- this._updateSrc(fileList[i].url, info.element, file);
968
- break;
969
- } else {
970
- this._produce(fileList[i].url, info.anchor, info.inputWidth, info.inputHeight, info.align, file, info.alt);
971
- }
972
- }
973
- }
974
-
975
- /**
976
- * @private
977
- * @description Uploads the image to the server.
978
- * @param {ImageInfo_image} info - Image upload info.
979
- * @param {FileList} files - List of image files.
980
- */
981
- _serverUpload(info, files) {
982
- if (!files) return;
983
-
984
- // server upload
985
- const imageUploadUrl = this.pluginOptions.uploadUrl;
986
- if (typeof imageUploadUrl === 'string' && imageUploadUrl.length > 0) {
987
- this.fileManager.upload(imageUploadUrl, this.pluginOptions.uploadHeaders, files, this.#UploadCallBack.bind(this, info), this._error.bind(this));
988
- } else {
989
- this._setBase64(files, info.anchor, info.inputWidth, info.inputHeight, info.align, info.alt, info.isUpdate);
990
- }
991
- }
992
-
993
- /**
994
- * @private
995
- * @description Converts an image file to Base64 and inserts it into the editor.
996
- * @param {FileList|File[]} files - List of image files.
997
- * @param {?Node} anchor - Optional anchor wrapping the image.
998
- * @param {string} width - Image width.
999
- * @param {string} height - Image height.
1000
- * @param {string} align - Image alignment.
1001
- * @param {string} alt - Alternative text.
1002
- * @param {boolean} isUpdate - Whether the image is being updated.
1003
- */
1004
- _setBase64(files, anchor, width, height, align, alt, isUpdate) {
1005
- try {
1006
- const filesLen = this.modal.isUpdate ? 1 : files.length;
1007
-
1008
- if (filesLen === 0) {
1009
- this.ui.hideLoading();
1010
- console.warn('[SUNEDITOR.image.base64.fail] cause : No applicable files');
1011
- return;
1012
- }
1013
-
1014
- this._base64RenderIndex = filesLen;
1015
- const filesStack = new Array(filesLen);
1016
-
1017
- if (this._resizing) {
1018
- this.inputX.value = width;
1019
- this.inputY.value = height;
1020
- }
1021
-
1022
- for (let i = 0, reader, file; i < filesLen; i++) {
1023
- reader = new FileReader();
1024
- file = files[i];
1025
-
1026
- reader.onload = function (on_reader, update, updateElement, on_file, index) {
1027
- filesStack[index] = {
1028
- result: on_reader.result,
1029
- file: on_file
1030
- };
1031
-
1032
- if (--this._base64RenderIndex === 0) {
1033
- this._onRenderBase64(update, filesStack, updateElement, anchor, width, height, align, alt);
1034
- this.ui.hideLoading();
1035
- }
1036
- }.bind(this, reader, isUpdate, this._element, file, i);
1037
- // se-ts-ignore
1038
- this._onRenderBase64;
1039
-
1040
- reader.readAsDataURL(file);
1041
- }
1042
- } catch (error) {
1043
- this.ui.hideLoading();
1044
- throw Error(`[SUNEDITOR.plugins.image._setBase64.fail] ${error.message}`);
1045
- }
1046
- }
1047
-
1048
- /**
1049
- * @private
1050
- * @description Inserts an image using a Base64-encoded string.
1051
- * @param {boolean} update - Whether the image is being updated.
1052
- * @param {Array<{result: string, file: { name: string, size: number }}>} filesStack - Stack of Base64-encoded files.
1053
- * - result: Image url or Base64-encoded string
1054
- * - file: File metadata ({ name: string, size: number })
1055
- * @param {HTMLImageElement} updateElement - The image element being updated.
1056
- * @param {?HTMLAnchorElement} anchor - Optional anchor wrapping the image.
1057
- * @param {string} width - Image width.
1058
- * @param {string} height - Image height.
1059
- * @param {string} align - Image alignment.
1060
- * @param {string} alt - Alternative text.
1061
- */
1062
- _onRenderBase64(update, filesStack, updateElement, anchor, width, height, align, alt) {
1063
- for (let i = 0, len = filesStack.length; i < len; i++) {
1064
- if (update) {
1065
- this._updateSrc(filesStack[i].result, updateElement, filesStack[i].file);
1066
- } else {
1067
- this._produce(filesStack[i].result, anchor, width, height, align, filesStack[i].file, alt);
1068
- }
1069
- }
1070
- }
1071
-
1072
- /**
1073
- * @private
1074
- * @description Wraps an image element with an anchor if provided.
1075
- * @param {Node} imgTag - The image element to be wrapped.
1076
- * @param {?Node} anchor - The anchor element to wrap around the image. If null, returns the image itself.
1077
- * @returns {Node} - The wrapped image inside the anchor or the original image element.
1078
- */
1079
- _setAnchor(imgTag, anchor) {
1080
- if (anchor) {
1081
- anchor.appendChild(imgTag);
1082
- return anchor;
1083
- }
1084
-
1085
- return imgTag;
1086
- }
1087
-
1088
- /**
1089
- * @private
1090
- * @description Handles errors during image upload and displays appropriate messages.
1091
- * @param {Object<string, *>} response - The error response from the server.
1092
- * @returns {Promise<void>}
1093
- */
1094
- async _error(response) {
1095
- const message = await this.triggerEvent('onImageUploadError', { error: response });
1096
- const err = message === NO_EVENT ? response.errorMessage : message || response.errorMessage;
1097
- this.ui.alertOpen(err, 'error');
1098
- console.error('[SUNEDITOR.plugin.image.error]', err);
1099
- }
1100
-
1101
- /**
1102
- * @description Handles the callback function for image upload completion.
1103
- * @param {ImageInfo_image} info - Image information.
1104
- * @param {XMLHttpRequest} xmlHttp - The XMLHttpRequest object.
1105
- */
1106
- async #UploadCallBack(info, xmlHttp) {
1107
- if ((await this.triggerEvent('imageUploadHandler', { xmlHttp, info })) === NO_EVENT) {
1108
- const response = JSON.parse(xmlHttp.responseText);
1109
- if (response.errorMessage) {
1110
- this._error(response);
1111
- } else {
1112
- this._register(info, response);
1113
- }
1114
- }
1115
- }
1116
-
1117
- #RemoveSelectedFiles() {
1118
- this.imgInputFile.value = '';
1119
- if (this.imgUrlFile) {
1120
- this.imgUrlFile.disabled = false;
1121
- this.previewSrc.style.textDecoration = '';
1122
- }
1123
-
1124
- // inputFile check
1125
- Modal.OnChangeFile(this.fileModalWrapper, []);
1126
- }
1127
-
1128
- #OnInputSize(xy, e) {
1129
- if (keyCodeMap.isSpace(e.code)) {
1130
- e.preventDefault();
1131
- return;
1132
- }
1133
-
1134
- if (xy === 'x' && this._onlyPercentage && e.target.value > 100) {
1135
- e.target.value = 100;
1136
- } else if (this.proportion.checked) {
1137
- const ratioSize = Figure.CalcRatio(this.inputX.value, this.inputY.value, this.sizeUnit, this._ratio);
1138
- if (xy === 'x') {
1139
- this.inputY.value = String(ratioSize.h);
1140
- } else {
1141
- this.inputX.value = String(ratioSize.w);
1142
- }
1143
- }
1144
- }
1145
-
1146
- #OnChangeRatio() {
1147
- this._ratio = this.proportion.checked
1148
- ? Figure.GetRatio(this.inputX.value, this.inputY.value, this.sizeUnit)
1149
- : {
1150
- w: 1,
1151
- h: 1
1152
- };
1153
- }
1154
-
1155
- #OnClickRevert() {
1156
- if (this._onlyPercentage) {
1157
- this.inputX.value = Number(this._origin_w) > 100 ? '100' : this._origin_w;
1158
- } else {
1159
- this.inputX.value = this._origin_w;
1160
- this.inputY.value = this._origin_h;
1161
- }
1162
- }
1163
-
1164
- #OnClickAsButton({ target }) {
1165
- this._activeAsInline(target.getAttribute('data-command') === 'asInline');
1166
- }
1167
-
1168
- #OnLinkPreview(e) {
1169
- const value = e.target.value.trim();
1170
- this._linkValue = this.previewSrc.textContent = !value
1171
- ? ''
1172
- : this.options.get('defaultUrlProtocol') && !value.includes('://') && value.indexOf('#') !== 0
1173
- ? this.options.get('defaultUrlProtocol') + value
1174
- : !value.includes('://')
1175
- ? '/' + value
1176
- : value;
1177
- }
1178
-
1179
- #OnfileInputChange({ target }) {
1180
- if (!this.imgInputFile.value) {
1181
- this.imgUrlFile.disabled = false;
1182
- this.previewSrc.style.textDecoration = '';
1183
- } else {
1184
- this.imgUrlFile.disabled = true;
1185
- this.previewSrc.style.textDecoration = 'line-through';
1186
- }
1187
-
1188
- // inputFile check
1189
- Modal.OnChangeFile(this.fileModalWrapper, target.files);
1190
- }
1191
-
1192
- #OpenGallery() {
1193
- this.plugins.imageGallery.open(this.#SetUrlInput.bind(this));
1194
- }
1195
-
1196
- #SetUrlInput(target) {
1197
- this.altText.value = target.getAttribute('data-value') || target.alt;
1198
- this._linkValue = this.previewSrc.textContent = this.imgUrlFile.value = target.getAttribute('data-command') || target.src;
1199
- this.imgUrlFile.focus();
1200
- }
1201
-
1202
- #OnloadImg(oImg, _svgDefaultSize, container) {
1203
- // svg exception handling
1204
- if (oImg.offsetWidth === 0) this._applySize(_svgDefaultSize, '');
1205
- if (this.options.get('componentAutoSelect')) {
1206
- this.component.select(oImg, Image_.key);
1207
- } else {
1208
- if (!this.component.isInline(container)) {
1209
- const line = this.format.addLine(container, null);
1210
- if (line) this.selection.setRange(line, 0, line, 0);
1211
- } else {
1212
- const r = this.selection.getNearRange(container);
1213
- if (r) {
1214
- this.selection.setRange(r.container, r.offset, r.container, r.offset);
1215
- } else {
1216
- this.component.select(oImg, Image_.key);
1217
- }
1218
- }
1219
- }
1220
-
1221
- this.editor._iframeAutoHeight(this.editor.frameContext);
1222
- this.history.push(false);
1223
-
1224
- delete oImg.onload;
1225
- }
1226
- }
1227
-
1228
- /**
1229
- * @typedef {Object} ModalReturns_image
1230
- * @property {HTMLElement} html
1231
- * @property {HTMLElement} alignForm
1232
- * @property {HTMLElement} fileModalWrapper
1233
- * @property {HTMLInputElement} imgInputFile
1234
- * @property {HTMLInputElement} imgUrlFile
1235
- * @property {HTMLInputElement} altText
1236
- * @property {HTMLInputElement} captionCheckEl
1237
- * @property {HTMLElement} previewSrc
1238
- * @property {HTMLElement} tabs
1239
- * @property {HTMLButtonElement} galleryButton
1240
- * @property {HTMLInputElement} proportion
1241
- * @property {HTMLInputElement} inputX
1242
- * @property {HTMLInputElement} inputY
1243
- * @property {HTMLButtonElement} revertBtn
1244
- * @property {HTMLButtonElement} asBlock
1245
- * @property {HTMLButtonElement} asInline
1246
- * @property {HTMLButtonElement} fileRemoveBtn
1247
- *
1248
- * @param {__se__EditorCore} editor
1249
- * @param {*} pluginOptions
1250
- * @returns {ModalReturns_image}
1251
- */
1252
- function CreateHTML_modal({ lang, icons, plugins }, pluginOptions) {
1253
- const createFileInputHtml = !pluginOptions.createFileInput
1254
- ? ''
1255
- : /*html*/ `
1256
- <div class="se-modal-form">
1257
- <label>${lang.image_modal_file}</label>
1258
- ${Modal.CreateFileInput({ icons, lang }, pluginOptions)}
1259
- </div>`;
1260
-
1261
- const createUrlInputHtml = !pluginOptions.createUrlInput
1262
- ? ''
1263
- : /*html*/ `
1264
- <div class="se-modal-form">
1265
- <label>${lang.image_modal_url}</label>
1266
- <div class="se-modal-form-files">
1267
- <input class="se-input-form se-input-url" data-focus type="text" />
1268
- ${
1269
- plugins.imageGallery
1270
- ? `<button type="button" class="se-btn se-tooltip se-modal-files-edge-button __se__gallery" aria-label="${lang.imageGallery}">
1271
- ${icons.image_gallery}
1272
- ${dom.utils.createTooltipInner(lang.imageGallery)}
1273
- </button>`
1274
- : ''
1275
- }
1276
- </div>
1277
- <pre class="se-link-preview"></pre>
1278
- </div>`;
1279
-
1280
- const canResizeHtml = !pluginOptions.canResize
1281
- ? ''
1282
- : /*html*/ `
1283
- <div class="se-modal-form">
1284
- <div class="se-modal-size-text">
1285
- <label class="size-w">${lang.width}</label>
1286
- <label class="se-modal-size-x">&nbsp;</label>
1287
- <label class="size-h">${lang.height}</label>
1288
- </div>
1289
- <input class="se-input-control _se_size_x" placeholder="auto" type="text" />
1290
- <label class="se-modal-size-x">x</label>
1291
- <input type="text" class="se-input-control _se_size_y" placeholder="auto" />
1292
- <label><input type="checkbox" class="se-modal-btn-check _se_check_proportion" checked/>&nbsp;${lang.proportion}</label>
1293
- <button type="button" aria-label="${lang.revert}" class="se-btn se-tooltip se-modal-btn-revert">
1294
- ${icons.revert}
1295
- ${dom.utils.createTooltipInner(lang.revert)}
1296
- </button>
1297
- </div>`;
1298
-
1299
- const useFormatTypeHtml = !pluginOptions.useFormatType
1300
- ? ''
1301
- : /*html*/ `
1302
- <div class="se-modal-form">
1303
- <div class="se-modal-flex-form">
1304
- <button type="button" data-command="asBlock" class="se-btn se-tooltip" aria-label="${lang.inlineStyle}">
1305
- ${icons.as_block}
1306
- ${dom.utils.createTooltipInner(lang.blockStyle)}
1307
- </button>
1308
- <button type="button" data-command="asInline" class="se-btn se-tooltip" aria-label="${lang.inlineStyle}">
1309
- ${icons.as_inline}
1310
- ${dom.utils.createTooltipInner(lang.inlineStyle)}
1311
- </button>
1312
- </div>
1313
- </div>`;
1314
-
1315
- const html = /*html*/ `
1316
- <div class="se-modal-header">
1317
- <button type="button" data-command="close" class="se-btn se-close-btn close" title="${lang.close}" aria-label="${lang.close}">${icons.cancel}</button>
1318
- <span class="se-modal-title">${lang.image_modal_title}</span>
1319
- </div>
1320
- <div class="se-modal-tabs">
1321
- <button type="button" class="_se_tab_link active" data-tab-link="image">${lang.image}</button>
1322
- <button type="button" class="_se_tab_link" data-tab-link="url">${lang.link}</button>
1323
- </div>
1324
- <form method="post" enctype="multipart/form-data">
1325
- <div class="_se_tab_content _se_tab_content_image">
1326
- <div class="se-modal-body">
1327
- ${createFileInputHtml}
1328
- ${createUrlInputHtml}
1329
- <div style="border-bottom: 1px dashed #ccc;"></div>
1330
- <div class="se-modal-form">
1331
- <label>${lang.image_modal_altText}</label><input class="se-input-form _se_image_alt" type="text" />
1332
- </div>
1333
- ${canResizeHtml}
1334
- ${useFormatTypeHtml}
1335
- <div class="se-modal-form se-modal-form-footer">
1336
- <label><input type="checkbox" class="se-modal-btn-check _se_image_check_caption" />&nbsp;${lang.caption}</label>
1337
- </div>
1338
- </div>
1339
- </div>
1340
- <div class="se-anchor-editor _se_tab_content _se_tab_content_url" style="display: none;">
1341
- </div>
1342
- <div class="se-modal-footer">
1343
- <div class="se-figure-align">
1344
- <label><input type="radio" name="suneditor_image_radio" class="se-modal-btn-radio" value="none" checked>${lang.basic}</label>
1345
- <label><input type="radio" name="suneditor_image_radio" class="se-modal-btn-radio" value="left">${lang.left}</label>
1346
- <label><input type="radio" name="suneditor_image_radio" class="se-modal-btn-radio" value="center">${lang.center}</label>
1347
- <label><input type="radio" name="suneditor_image_radio" class="se-modal-btn-radio" value="right">${lang.right}</label>
1348
- </div>
1349
- <button type="submit" class="se-btn-primary" title="${lang.submitButton}" aria-label="${lang.submitButton}"><span>${lang.submitButton}</span></button>
1350
- </div>
1351
- </form>`;
1352
-
1353
- const content = dom.utils.createElement('DIV', { class: 'se-modal-content' }, html);
1354
-
1355
- return {
1356
- html: content,
1357
- alignForm: content.querySelector('.se-figure-align'),
1358
- fileModalWrapper: content.querySelector('.se-flex-input-wrapper'),
1359
- imgInputFile: content.querySelector('.__se__file_input'),
1360
- imgUrlFile: content.querySelector('.se-input-url'),
1361
- altText: content.querySelector('._se_image_alt'),
1362
- captionCheckEl: content.querySelector('._se_image_check_caption'),
1363
- previewSrc: content.querySelector('._se_tab_content_image .se-link-preview'),
1364
- tabs: content.querySelector('.se-modal-tabs'),
1365
- galleryButton: content.querySelector('.__se__gallery'),
1366
- proportion: content.querySelector('._se_check_proportion'),
1367
- inputX: content.querySelector('._se_size_x'),
1368
- inputY: content.querySelector('._se_size_y'),
1369
- revertBtn: content.querySelector('.se-modal-btn-revert'),
1370
- asBlock: content.querySelector('[data-command="asBlock"]'),
1371
- asInline: content.querySelector('[data-command="asInline"]'),
1372
- fileRemoveBtn: content.querySelector('.se-file-remove')
1373
- };
1374
- }
1375
-
1376
- export default Image_;